Skip to content

Commit b4117ae

Browse files
committed
Run postconditions before the 'return'
So they correctly see the pre-moved named return value(s) The following example now works correctly - before this commit, the postconditions would fail because the moved-from strings were zero length because of the automatic move-from-definite-last-use of the named return values make_string: () -> (ret: std::string = "xyzzy") post (ret.length() == ret.length()$ + 5) = { ret += " and "; } make_strings: () -> ( a: std::string = "xyzzy", b: std::string = "plugh" ) post (a.length() == b.length() == 5) = { } main: () = { std::cout << make_string() + "plugh\n"; std::cout << make_strings().a + make_strings().b + "\n"; }
1 parent 13f765b commit b4117ae

14 files changed

+155
-20
lines changed

include/cpp2util.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@
242242
#if defined(__cpp_lib_format) || (defined(_MSC_VER) && _MSC_VER >= 1929)
243243
#include <format>
244244
#endif
245+
#include <functional>
245246
#include <iostream>
246247
#include <iterator>
247248
#include <limits>
@@ -1576,6 +1577,11 @@ constexpr auto as( X const& x ) -> decltype(auto)
15761577
// finally_success ensures something is run at the end of a scope
15771578
// if no exception is thrown
15781579
//
1580+
// finally_presuccess ensures a group of add'd operations are run
1581+
// immediately before (not after) the return if no exception is
1582+
// thrown - right now this is used only for postconditions, so
1583+
// they can inspect named return values before they're moved from
1584+
//
15791585
//-----------------------------------------------------------------------
15801586
//
15811587

@@ -1631,6 +1637,39 @@ class finally
16311637
};
16321638

16331639

1640+
class finally_presuccess
1641+
{
1642+
public:
1643+
finally_presuccess() = default;
1644+
1645+
auto add(const auto& f) { fs.push_back(f); }
1646+
1647+
// In compiled Cpp2 code, this function will be called
1648+
// immediately before 'return' (both explicit and implicit)
1649+
auto run() {
1650+
if (invoke && ecount == std::uncaught_exceptions()) {
1651+
for (auto const& f : fs) {
1652+
f();
1653+
}
1654+
}
1655+
invoke = false;
1656+
}
1657+
1658+
~finally_presuccess() noexcept {
1659+
run();
1660+
}
1661+
1662+
finally_presuccess(finally_presuccess const&) = delete;
1663+
void operator= (finally_presuccess const&) = delete;
1664+
void operator= (finally_presuccess &&) = delete;
1665+
1666+
private:
1667+
std::vector<std::function<void()>> fs;
1668+
int ecount = std::uncaught_exceptions();
1669+
bool invoke = true;
1670+
};
1671+
1672+
16341673
//-----------------------------------------------------------------------
16351674
//
16361675
// args: see main() arguments as vector<string_view>

regression-tests/mixed-postexpression-with-capture.cpp2

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
main: () -> int = {
99
insert_at( 0, 42 );
10+
std::cout << make_string() + "plugh\n";
11+
std::cout << make_strings().a + make_strings().b + "\n";
1012
}
1113

1214
vec: std::vector<int> = ();
@@ -17,3 +19,20 @@ insert_at: (where: int, val: int)
1719
= {
1820
vec.push_back(val);
1921
}
22+
23+
make_string: () -> (ret: std::string = "xyzzy")
24+
post (ret.length() == ret.length()$ + 5)
25+
= {
26+
ret += " and ";
27+
}
28+
29+
make_strings: ()
30+
-> (
31+
a: std::string = "xyzzy",
32+
b: std::string = "plugh"
33+
)
34+
post (a.length() == b.length() == 5)
35+
= {
36+
// 'return' is generated when omitted like this
37+
}
38+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
xyzzy and plugh
2+
xyzzyplugh
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
xyzzy and plugh
2+
xyzzyplugh

regression-tests/test-results/gcc-10/pure2-bugfix-for-requires-clause-in-forward-declaration.cpp.output

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:46: error: expect
66
In file included from pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:7:
77
../../../include/cpp2util.h:10005:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
88
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:4:1: note: in expansion of macro ‘CPP2_REQUIRES_’
9-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:90&&) requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type, std::__cxx11::string>’
9+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:91&&) requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type, std::__cxx11::string>’
1010
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:11: note: candidates are: ‘element::element(const element&)’
11-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template<class auto:88> element::element(auto:88&&)’
11+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template<class auto:89> element::element(auto:89&&)’
1212
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here
1313
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:78: error: expected unqualified-id before ‘{’ token
14-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:91&&) requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type, std::__cxx11::string>’
14+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:92&&) requires is_same_v<typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type, std::__cxx11::string>’
1515
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:6:16: note: candidates are: ‘void element::operator=(const element&)’
16-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:89> element& element::operator=(auto:89&&)’
16+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:90> element& element::operator=(auto:90&&)’
1717
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here

regression-tests/test-results/gcc-10/pure2-print.cpp.output

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ pure2-print.cpp2:66:1: note: in expansion of macro ‘CPP2_REQUIRES_’
99
pure2-print.cpp2:94:1: note: in expansion of macro ‘CPP2_REQUIRES_’
1010
pure2-print.cpp2:7:41: error: ‘constexpr const T outer::object_alias’ is not a static data member of ‘class outer’
1111
pure2-print.cpp2:7:48: error: template definition of non-template ‘constexpr const T outer::object_alias’
12-
pure2-print.cpp2:65:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:97& ...) requires (is_convertible_v<typename std::remove_cv<typename std::remove_reference<decltype(outer::mytype::variadic::x)>::type>::type, int> && ...)’
13-
pure2-print.cpp2:65:29: note: candidate is: ‘template<class ... auto:88> static void outer::mytype::variadic(const auto:88& ...)’
12+
pure2-print.cpp2:65:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:98& ...) requires (is_convertible_v<typename std::remove_cv<typename std::remove_reference<decltype(outer::mytype::variadic::x)>::type>::type, int> && ...)’
13+
pure2-print.cpp2:65:29: note: candidate is: ‘template<class ... auto:89> static void outer::mytype::variadic(const auto:89& ...)’
1414
pure2-print.cpp2:8:19: note: ‘class outer::mytype’ defined here
1515
pure2-print.cpp2:93:37: error: no declaration matches ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::cmp_greater_eq(sizeof (Args)..., 0)’
1616
pure2-print.cpp2:93:37: note: no functions named ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::cmp_greater_eq(sizeof (Args)..., 0)’
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
xyzzy and plugh
2+
xyzzyplugh

regression-tests/test-results/mixed-captures-in-expressions-and-postconditions.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ auto insert_at(cpp2::in<int> where, cpp2::in<int> val) -> void
4646

4747
#line 22 "mixed-captures-in-expressions-and-postconditions.cpp2"
4848
{
49+
cpp2::finally_presuccess cpp2_finally_presuccess;
4950
cpp2::Default.expects(cpp2::cmp_less_eq(0,where) && cpp2::cmp_less_eq(where,CPP2_UFCS_0(ssize, vec)), "");
50-
auto post_21_5 = cpp2::finally_success([&, _1 = CPP2_UFCS_0(ssize, vec)]{cpp2::Default.expects(CPP2_UFCS_0(ssize, vec) == _1 + 1, "");} );
51+
cpp2_finally_presuccess.add([&, _1 = CPP2_UFCS_0(ssize, vec)]{cpp2::Default.expects(CPP2_UFCS_0(ssize, vec) == _1 + 1, "");} );
5152
#line 23 "mixed-captures-in-expressions-and-postconditions.cpp2"
5253
static_cast<void>(CPP2_UFCS(insert, vec, CPP2_UFCS_0(begin, vec) + where, val));
5354
}

regression-tests/test-results/mixed-postexpression-with-capture.cpp

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,24 @@
2121
#line 8 "mixed-postexpression-with-capture.cpp2"
2222
[[nodiscard]] auto main() -> int;
2323

24-
#line 12 "mixed-postexpression-with-capture.cpp2"
24+
#line 14 "mixed-postexpression-with-capture.cpp2"
2525
extern std::vector<int> vec;
2626

2727
auto insert_at(cpp2::in<int> where, cpp2::in<int> val) -> void;
28+
using make_string_ret = std::string;
29+
30+
31+
#line 23 "mixed-postexpression-with-capture.cpp2"
32+
[[nodiscard]] auto make_string() -> make_string_ret;
33+
struct make_strings_ret { std::string a; std::string b; };
34+
35+
36+
37+
#line 29 "mixed-postexpression-with-capture.cpp2"
38+
[[nodiscard]] auto make_strings() -> make_strings_ret;
39+
#line 38 "mixed-postexpression-with-capture.cpp2"
40+
41+
#line 1 "mixed-postexpression-with-capture.cpp2"
2842

2943
//=== Cpp2 function definitions =================================================
3044

@@ -33,17 +47,45 @@ auto insert_at(cpp2::in<int> where, cpp2::in<int> val) -> void;
3347
#line 8 "mixed-postexpression-with-capture.cpp2"
3448
[[nodiscard]] auto main() -> int{
3549
insert_at(0, 42);
50+
std::cout << make_string() + "plugh\n";
51+
std::cout << make_strings().a + make_strings().b + "\n";
3652
}
3753

3854
std::vector<int> vec {};
3955

4056
auto insert_at(cpp2::in<int> where, cpp2::in<int> val) -> void
4157

42-
#line 17 "mixed-postexpression-with-capture.cpp2"
58+
#line 19 "mixed-postexpression-with-capture.cpp2"
4359
{
60+
cpp2::finally_presuccess cpp2_finally_presuccess;
4461
cpp2::Default.expects(cpp2::cmp_less_eq(0,where) && cpp2::cmp_less_eq(where,CPP2_UFCS_0(ssize, vec)), "");
45-
auto post_16_5 = cpp2::finally_success([&, _1 = CPP2_UFCS_0(size, vec)]{cpp2::Default.expects(CPP2_UFCS_0(size, vec) == _1 + 1, "");} );
46-
#line 18 "mixed-postexpression-with-capture.cpp2"
62+
cpp2_finally_presuccess.add([&, _1 = CPP2_UFCS_0(size, vec)]{cpp2::Default.expects(CPP2_UFCS_0(size, vec) == _1 + 1, "");} );
63+
#line 20 "mixed-postexpression-with-capture.cpp2"
4764
CPP2_UFCS(push_back, vec, val);
4865
}
4966

67+
[[nodiscard]] auto make_string() -> make_string_ret
68+
69+
{
70+
cpp2::finally_presuccess cpp2_finally_presuccess;
71+
std::string ret {"xyzzy"};
72+
cpp2_finally_presuccess.add([&, _1 = CPP2_UFCS_0(length, ret)]{cpp2::Default.expects(CPP2_UFCS_0(length, ret) == _1 + 5, "");} );
73+
#line 26 "mixed-postexpression-with-capture.cpp2"
74+
ret += " and ";
75+
cpp2_finally_presuccess.run(); return std::move(ret); }
76+
77+
[[nodiscard]] auto make_strings() -> make_strings_ret
78+
79+
#line 35 "mixed-postexpression-with-capture.cpp2"
80+
{
81+
cpp2::finally_presuccess cpp2_finally_presuccess;
82+
std::string a {"xyzzy"};
83+
std::string b {"plugh"};
84+
cpp2_finally_presuccess.add([&]{cpp2::Default.expects([_0 = CPP2_UFCS_0(length, a), _1 = CPP2_UFCS_0(length, b), _2 = 5]{ return _0==_1 && _1==_2; }(), "");} );
85+
#line 30 "mixed-postexpression-with-capture.cpp2"
86+
cpp2_finally_presuccess.run(); return { std::move(a), std::move(b) };
87+
88+
#line 36 "mixed-postexpression-with-capture.cpp2"
89+
// 'return' is generated when omitted like this
90+
}
91+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
xyzzy and plugh
2+
xyzzyplugh

0 commit comments

Comments
 (0)