Skip to content

Commit 92aa71e

Browse files
authored
Fix for downcast to parent. (#501)
* Fix for downcast to parent. * Regression tests for down and upcast in class hierarchy.
1 parent 5a252cc commit 92aa71e

7 files changed

+276
-4
lines changed

include/cpp2util.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -975,9 +975,15 @@ auto as( X const& x ) -> decltype(auto) {
975975
return x;
976976
}
977977

978+
template< typename C, typename X >
979+
requires std::is_same_v<C, X>
980+
auto as( X& x ) -> decltype(auto) {
981+
return x;
982+
}
983+
978984
template< typename C, typename X >
979985
auto as( X const& x ) -> auto
980-
requires (!std::is_same_v<C, X> && requires { C{x}; })
986+
requires (!std::is_same_v<C, X> && !std::is_base_of_v<C, X> && requires { C{x}; })
981987
{
982988
// Experiment: Recognize the nested `::value_type` pattern for some dynamic library types
983989
// like std::optional, and try to prevent accidental narrowing conversions even when
@@ -991,9 +997,15 @@ auto as( X const& x ) -> auto
991997
}
992998

993999
template< typename C, typename X >
994-
requires std::is_base_of_v<C, X>
995-
auto as( X&& x ) -> C&& {
996-
return CPP2_FORWARD(x);
1000+
requires (std::is_base_of_v<C, X> && !std::is_same_v<C, X>)
1001+
auto as( X& x ) -> C& {
1002+
return x;
1003+
}
1004+
1005+
template< typename C, typename X >
1006+
requires (std::is_base_of_v<C, X> && !std::is_same_v<C, X>)
1007+
auto as( X const& x ) -> C const& {
1008+
return x;
9971009
}
9981010

9991011
template< typename C, typename X >
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
A: type = {
2+
public i: int = 0;
3+
4+
const_foo: (virtual this) = { std::cout << "const foo \n"; }
5+
mut_foo: (inout this) = { std::cout << "foo \n"; }
6+
}
7+
8+
B: type = {
9+
this: A = ();
10+
public d: double = 0.0;
11+
}
12+
13+
func_mut: (inout a: A) -> void = { std::cout << "Call A mut: (a.i)$" << std::endl; }
14+
func_mut: (inout b: B) -> void = { std::cout << "Call B mut: (b.d)$" << std::endl; }
15+
func_const: (a: A) -> void = { std::cout << "Call A const: (a.i)$" << std::endl; }
16+
func_const: (b: B) -> void = { std::cout << "Call B const: (b.d)$" << std::endl; }
17+
18+
test_const_foo: () = {
19+
s: A =();
20+
sC: *const A = s&;
21+
s.const_foo();
22+
sC*.const_foo();
23+
(s as A).const_foo();
24+
(sC* as A).const_foo();
25+
_ = s;
26+
_ = sC;
27+
}
28+
29+
test_mut_foo: () = {
30+
s: A =();
31+
s.mut_foo();
32+
(s as A).mut_foo();
33+
_ = s;
34+
}
35+
36+
test_up: () = {
37+
b: B = ();
38+
bC: *const B = b&;
39+
40+
func_const(b);
41+
func_const(b as B);
42+
func_const(b as A);
43+
func_const(bC*);
44+
func_const(bC* as B);
45+
func_const(bC* as A);
46+
47+
func_mut(b);
48+
func_mut(b as B);
49+
func_mut(b as A);
50+
51+
_ = b;
52+
_ = bC;
53+
}
54+
55+
test_down: () = {
56+
b: B = ();
57+
bC: *const B = b&;
58+
a: *A = (b as A)&;
59+
aC: *const A = (b as A)&;
60+
61+
func_const(a*);
62+
func_const(a* as B);
63+
func_const(a* as A);
64+
func_const(aC*);
65+
func_const(aC* as B);
66+
func_const(aC* as A);
67+
func_mut(a*);
68+
func_mut(a* as B);
69+
func_mut(a* as A);
70+
71+
_ = b;
72+
_ = bC;
73+
_ = a;
74+
_ = aC;
75+
}
76+
77+
main: () -> int = {
78+
79+
test_const_foo();
80+
test_mut_foo();
81+
test_up();
82+
test_down();
83+
84+
return 0;
85+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const foo
2+
const foo
3+
const foo
4+
const foo
5+
foo
6+
foo
7+
Call B const: 0.000000
8+
Call B const: 0.000000
9+
Call A const: 0
10+
Call B const: 0.000000
11+
Call B const: 0.000000
12+
Call A const: 0
13+
Call B mut: 0.000000
14+
Call B mut: 0.000000
15+
Call A mut: 0
16+
Call A const: 0
17+
Call B const: 0.000000
18+
Call A const: 0
19+
Call A const: 0
20+
Call B const: 0.000000
21+
Call A const: 0
22+
Call A mut: 0
23+
Call B mut: 0.000000
24+
Call A mut: 0

regression-tests/test-results/gcc-10/pure2-types-down-upcast.cpp.output

Whitespace-only changes.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
2+
#define CPP2_USE_MODULES Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
#line 1 "pure2-types-down-upcast.cpp2"
10+
class A;
11+
12+
13+
#line 8 "pure2-types-down-upcast.cpp2"
14+
class B;
15+
16+
17+
//=== Cpp2 type definitions and function declarations ===========================
18+
19+
#line 1 "pure2-types-down-upcast.cpp2"
20+
class A {
21+
public: int i {0};
22+
23+
public: virtual auto const_foo() const -> void;
24+
public: auto mut_foo() -> void;
25+
26+
public: A() = default;
27+
public: A(A const&) = delete; /* No 'that' constructor, suppress copy */
28+
public: auto operator=(A const&) -> void = delete;
29+
#line 6 "pure2-types-down-upcast.cpp2"
30+
};
31+
32+
class B: public A {
33+
34+
public: double d {0.0};
35+
public: B() = default;
36+
public: B(B const&) = delete; /* No 'that' constructor, suppress copy */
37+
public: auto operator=(B const&) -> void = delete;
38+
39+
#line 11 "pure2-types-down-upcast.cpp2"
40+
};
41+
42+
auto func_mut(A& a) -> void;
43+
auto func_mut(B& b) -> void;
44+
auto func_const(cpp2::in<A> a) -> void;
45+
auto func_const(cpp2::in<B> b) -> void;
46+
47+
auto test_const_foo() -> void;
48+
49+
50+
#line 29 "pure2-types-down-upcast.cpp2"
51+
auto test_mut_foo() -> void;
52+
53+
54+
#line 36 "pure2-types-down-upcast.cpp2"
55+
auto test_up() -> void;
56+
57+
58+
#line 55 "pure2-types-down-upcast.cpp2"
59+
auto test_down() -> void;
60+
61+
62+
#line 77 "pure2-types-down-upcast.cpp2"
63+
[[nodiscard]] auto main() -> int;
64+
65+
66+
//=== Cpp2 function definitions =================================================
67+
68+
69+
#line 4 "pure2-types-down-upcast.cpp2"
70+
auto A::const_foo() const -> void{std::cout << "const foo \n"; }
71+
auto A::mut_foo() -> void{std::cout << "foo \n"; }
72+
73+
#line 13 "pure2-types-down-upcast.cpp2"
74+
auto func_mut(A& a) -> void {std::cout << "Call A mut: " + cpp2::to_string(a.i) << std::endl;}
75+
auto func_mut(B& b) -> void {std::cout << "Call B mut: " + cpp2::to_string(b.d) << std::endl;}
76+
auto func_const(cpp2::in<A> a) -> void{std::cout << "Call A const: " + cpp2::to_string(a.i) << std::endl;}
77+
auto func_const(cpp2::in<B> b) -> void{std::cout << "Call B const: " + cpp2::to_string(b.d) << std::endl;}
78+
79+
auto test_const_foo() -> void{
80+
A s {};
81+
A const* sC {&s};
82+
CPP2_UFCS_0(const_foo, s);
83+
CPP2_UFCS_0(const_foo, (*cpp2::assert_not_null(sC)));
84+
CPP2_UFCS_0(const_foo, (cpp2::as_<A>(s)));
85+
CPP2_UFCS_0(const_foo, (cpp2::as_<A>(*cpp2::assert_not_null(sC))));
86+
(void) std::move(s);
87+
(void) std::move(sC);
88+
}
89+
90+
auto test_mut_foo() -> void{
91+
A s {};
92+
CPP2_UFCS_0(mut_foo, s);
93+
CPP2_UFCS_0(mut_foo, (cpp2::as_<A>(s)));
94+
(void) std::move(s);
95+
}
96+
97+
auto test_up() -> void{
98+
B b {};
99+
B const* bC {&b};
100+
101+
func_const(b);
102+
func_const(cpp2::as_<B>(b));
103+
func_const(cpp2::as_<A>(b));
104+
func_const(*cpp2::assert_not_null(bC));
105+
func_const(cpp2::as_<B>(*cpp2::assert_not_null(bC)));
106+
func_const(cpp2::as_<A>(*cpp2::assert_not_null(bC)));
107+
108+
func_mut(b);
109+
func_mut(cpp2::as_<B>(b));
110+
func_mut(cpp2::as_<A>(b));
111+
112+
(void) std::move(b);
113+
(void) std::move(bC);
114+
}
115+
116+
auto test_down() -> void{
117+
B b {};
118+
B const* bC {&b};
119+
A* a {&(cpp2::as_<A>(b))};
120+
A const* aC {&(cpp2::as_<A>(b))};
121+
122+
func_const(*cpp2::assert_not_null(a));
123+
func_const(cpp2::as_<B>(*cpp2::assert_not_null(a)));
124+
func_const(cpp2::as_<A>(*cpp2::assert_not_null(a)));
125+
func_const(*cpp2::assert_not_null(aC));
126+
func_const(cpp2::as_<B>(*cpp2::assert_not_null(aC)));
127+
func_const(cpp2::as_<A>(*cpp2::assert_not_null(aC)));
128+
func_mut(*cpp2::assert_not_null(a));
129+
func_mut(cpp2::as_<B>(*cpp2::assert_not_null(a)));
130+
func_mut(cpp2::as_<A>(*cpp2::assert_not_null(a)));
131+
132+
(void) std::move(b);
133+
(void) std::move(bC);
134+
(void) std::move(a);
135+
(void) std::move(aC);
136+
}
137+
138+
[[nodiscard]] auto main() -> int{
139+
140+
test_const_foo();
141+
test_mut_foo();
142+
test_up();
143+
test_down();
144+
145+
return 0;
146+
}
147+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-types-down-upcast.cpp2... ok (all Cpp2, passes safety checks)
2+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-types-down-upcast.cpp2... ok (all Cpp2, passes safety checks)
2+

0 commit comments

Comments
 (0)