Skip to content

Commit 2649d72

Browse files
committed
feat: support function types
Add _function-type_ as an alternative production of _type-id_. A parameter's identifier must be named `_`. `throws` needs to be specified for non Cpp1-`noexcept` function types. Some examples: ```Cpp2 main: () = { f0: * () throws = :() = {}; f7: * (move _: i32) throws = :(move x: i32) = {}; [[assert: inspect f0 -> bool { is () = (std::terminate(), false); is * () throws = true; is _ = false; }]] } // Test case from #343. f2: () -> std::function<(_: std::string) throws -> std::string> = { return :(s: std::string) -> std::string = { return s + " World!"; }; } // Adapted from <https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-operators>. f: (x: i32) -> * (_: i32) throws -> std::string = +:(x: i32) -> std::string = ""; postfix_operators: () = { [[assert: f is (_: i32) throws -> * (_: i32) throws -> std::string]] // / | | // / | | [[assert: f(42) is * (_: i32) throws -> std::string]] // ________________/ | // / | [[assert: f(42)* is (_: i32) throws -> std::string]] // _______________/ // / [[assert: (f(42)*)(1) is std::string]] } ```
1 parent 5a57f5e commit 2649d72

21 files changed

+631
-105
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
main: (args) = { _ = :* int = args.argc&; }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
main: () = {
2+
f: * (x: i32) throws = :(_) = {};
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
main: () = {
2+
f: * () [[pre: true]] = :() = {};
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
main: () = { f: * () -> (x: i32); }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
main: () = {
2+
f: * (_) throws = :(_) = {};
3+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
main: () = {
2+
postfix_operators();
3+
4+
// Variables with type of a mix of `*`/`const` to `() throws -> void`.
5+
f0: * () throws = :() = {};
6+
f1: const * () throws = f0;
7+
f2: * const () throws = f0;
8+
f3: const * const () throws = f0;
9+
10+
// Uninitialized.
11+
f4: * () throws;
12+
f4 = f0;
13+
14+
f10: * * () throws = f0&;
15+
f11: const * * () throws = f10;
16+
f12: * const * () throws = f10;
17+
f13: const * const * () throws = f10;
18+
19+
i: i32 = 0;
20+
i0: * i32 = i&;
21+
i1: const * i32 = i0;
22+
i2: * const i32 = i0;
23+
i3: const * const i32 = i0;
24+
25+
// Assert consistent '*'/'const' with non-function type variables.
26+
static_assert((std::is_const_v<decltype(f10)>) == std::is_const_v<decltype(i0)>);
27+
static_assert((std::is_const_v<decltype(f11)>) == std::is_const_v<decltype(i1)>);
28+
static_assert((std::is_const_v<decltype(f12)>) == std::is_const_v<decltype(i2)>);
29+
static_assert((std::is_const_v<decltype(f13)>) == std::is_const_v<decltype(i3)>);
30+
_ = f10;
31+
_ = f11;
32+
_ = f12;
33+
_ = f13;
34+
_ = i0;
35+
_ = i1;
36+
_ = i2;
37+
_ = i3;
38+
39+
// Variables with various kinds of parameter.
40+
f5: * (_: i32) throws = :(x: i32) = {};
41+
f6: * (_: std::any) throws = :(x: std::any) = {};
42+
f7: * (move _: i32) throws = :(move x: i32) = {};
43+
f8: * (out _: i32) throws = :(copy x) = {};
44+
45+
// In alternative.
46+
[[assert: inspect f0 -> bool {
47+
is () = (std::terminate(), false);
48+
is () throws = (std::terminate(), false);
49+
is * () throws = true;
50+
is _ = false;
51+
}]]
52+
[[assert: inspect f0* -> bool {
53+
is () throws = true;
54+
is _ = false;
55+
}]]
56+
57+
// As block variable.
58+
(f: * () throws = f0) { }
59+
(f: () throws = f0*) { }
60+
61+
// As local function parameter.
62+
_ = :(f: * () throws) = {};
63+
_ = :(f: * () throws -> * () throws) = {};
64+
_ = :(f: * () throws -> * () throws -> * () throws) = {};
65+
66+
// In local function return type.
67+
_ = :() -> * () throws = nullptr;
68+
_ = :() -> * () throws -> * () throws = nullptr;
69+
70+
// Without `throws`.
71+
_ = :* (copy _: std::string_view, copy _: CPP2_MESSAGE_PARAM) = cpp2::report_and_terminate;
72+
73+
// As template argument.
74+
_ = :std::type_identity_t<* () throws> = f0;
75+
static_assert(std::is_function_v<() throws>);
76+
}
77+
78+
// As non-local function parameter.
79+
g: (f: * () throws) = { }
80+
g: (f: * () throws -> * () throws) = { }
81+
// As template parameter.
82+
g: <V: * () throws> () = { }
83+
g: <V: * () throws -> * () throws> () = { }
84+
85+
// In non-local function return type.
86+
g1: () -> * () throws = nullptr;
87+
g2: () -> * () throws -> * () throws = nullptr;
88+
89+
// clang-format off
90+
// Test case from #343.
91+
f2: () -> std::function<(_: std::string) throws -> std::string> = {
92+
return :(s: std::string) -> std::string = { return s + " World!"; };
93+
}
94+
95+
// Adapted from <https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-operators>.
96+
f: (x: i32) -> * (_: i32) throws -> std::string = +:(x: i32) -> std::string = "";
97+
postfix_operators: () = {
98+
[[assert: f is (_: i32) throws -> * (_: i32) throws -> std::string]]
99+
// / | |
100+
// / | |
101+
[[assert: f(42) is * (_: i32) throws -> std::string]]
102+
// ________________/ |
103+
// / |
104+
[[assert: f(42)* is (_: i32) throws -> std::string]]
105+
// _______________/
106+
// /
107+
[[assert: (f(42)*)(1) is std::string]]
108+
} // clang-format on

regression-tests/test-results/gcc-13/pure2-bugfix-for-multi-token-type-prvalue.cpp.execution

Whitespace-only changes.

regression-tests/test-results/gcc-13/pure2-bugfix-for-multi-token-type-prvalue.cpp.output

Whitespace-only changes.

regression-tests/test-results/gcc-13/pure2-function-type-id.cpp.execution

Whitespace-only changes.

regression-tests/test-results/gcc-13/pure2-function-type-id.cpp.output

Whitespace-only changes.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
#define CPP2_USE_MODULES Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
10+
11+
//=== Cpp2 type definitions and function declarations ===========================
12+
13+
#line 1 "pure2-bugfix-for-multi-token-type-prvalue.cpp2"
14+
auto main(int const argc_, char const* const* const argv_) -> int;
15+
16+
17+
//=== Cpp2 function definitions =================================================
18+
19+
#line 1 "pure2-bugfix-for-multi-token-type-prvalue.cpp2"
20+
auto main(int const argc_, char const* const* const argv_) -> int{
21+
auto args = cpp2::make_args(argc_, argv_);
22+
#line 1 "pure2-bugfix-for-multi-token-type-prvalue.cpp2"
23+
(void) std::type_identity_t<int*>{&args.argc}; }
24+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-bugfix-for-multi-token-type-prvalue.cpp2... ok (all Cpp2, passes safety checks)
2+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pure2-function-type-id-2-error.cpp2...
2+
pure2-function-type-id-2-error.cpp2(2,9): error: the parameter of a function type must be named '_'
3+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pure2-function-type-id-3-error.cpp2...
2+
pure2-function-type-id-3-error.cpp2(2,11): error: a function type can't have contracts
3+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pure2-function-type-id-4-error.cpp2...
2+
pure2-function-type-id-4-error.cpp2(1,25): error: a function type can't have an anonymous return type
3+
pure2-function-type-id-4-error.cpp2(1,14): error: f - variable must be initialized on every branch path
4+
==> program violates initialization safety guarantee - see previous errors
5+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pure2-function-type-id-5-error.cpp2...
2+
pure2-function-type-id-5-error.cpp2(2,9): error: function type parameter must have a type
3+
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
2+
#define CPP2_USE_MODULES Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
10+
11+
//=== Cpp2 type definitions and function declarations ===========================
12+
13+
#line 1 "pure2-function-type-id.cpp2"
14+
auto main() -> int;
15+
16+
17+
#line 78 "pure2-function-type-id.cpp2"
18+
// As non-local function parameter.
19+
auto g(auto (* f)() -> void) -> void;
20+
auto g(auto (* f)() -> auto (*)() -> void) -> void;
21+
// As template parameter.
22+
template<auto (* V)() -> void> auto g() -> void;
23+
template<auto (* V)() -> auto (*)() -> void> auto g() -> void;
24+
25+
// In non-local function return type.
26+
[[nodiscard]] auto g1() -> auto (*)() -> void;
27+
[[nodiscard]] auto g2() -> auto (*)() -> auto (*)() -> void;
28+
29+
// clang-format off
30+
// Test case from #343.
31+
[[nodiscard]] auto f2() -> std::function<auto (cpp2::in<std::string>) -> std::string>;
32+
33+
34+
#line 95 "pure2-function-type-id.cpp2"
35+
// Adapted from <https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-operators>.
36+
[[nodiscard]] auto f(cpp2::in<cpp2::i32> x) -> auto (*)(cpp2::in<cpp2::i32>) -> std::string;
37+
auto postfix_operators() -> void;
38+
39+
40+
//=== Cpp2 function definitions =================================================
41+
42+
#line 1 "pure2-function-type-id.cpp2"
43+
auto main() -> int{
44+
postfix_operators();
45+
46+
// Variables with type of a mix of `*`/`const` to `() throws -> void`.
47+
std::type_identity_t<auto (*)() -> void> f0 {[]() -> void{}};
48+
std::type_identity_t<auto (*)() -> void> const f1 {f0};
49+
std::type_identity_t<auto (* const)() -> void> f2 {f0};
50+
std::type_identity_t<auto (* const)() -> void> const f3 {f0};
51+
52+
// Uninitialized.
53+
cpp2::deferred_init<std::type_identity_t<auto (*)() -> void>> f4;
54+
f4.construct(f0);
55+
56+
std::type_identity_t<auto (**)() -> void> f10 {&f0};
57+
std::type_identity_t<auto (**)() -> void> const f11 {f10};
58+
std::type_identity_t<auto (* const*)() -> void> f12 {f10};
59+
std::type_identity_t<auto (* const*)() -> void> const f13 {f10};
60+
61+
cpp2::i32 i {0};
62+
cpp2::i32* i0 {&i};
63+
cpp2::i32* const i1 {i0};
64+
cpp2::i32 const* i2 {i0};
65+
cpp2::i32 const* const i3 {i0};
66+
67+
// Assert consistent '*'/'const' with non-function type variables.
68+
static_assert((std::is_const_v<decltype(f10)>)==std::is_const_v<decltype(i0)>);
69+
static_assert((std::is_const_v<decltype(f11)>)==std::is_const_v<decltype(i1)>);
70+
static_assert((std::is_const_v<decltype(f12)>)==std::is_const_v<decltype(i2)>);
71+
static_assert((std::is_const_v<decltype(f13)>)==std::is_const_v<decltype(i3)>);
72+
(void) std::move(f10);
73+
(void) std::move(f11);
74+
(void) std::move(f12);
75+
(void) std::move(f13);
76+
(void) std::move(i0);
77+
(void) std::move(i1);
78+
(void) std::move(i2);
79+
(void) std::move(i3);
80+
81+
// Variables with various kinds of parameter.
82+
std::type_identity_t<auto (*)(cpp2::in<cpp2::i32>) -> void> f5 {[](cpp2::in<cpp2::i32> x) -> void{}};
83+
std::type_identity_t<auto (*)(cpp2::in<std::any>) -> void> f6 {[](cpp2::in<std::any> x) -> void{}};
84+
std::type_identity_t<auto (*)(cpp2::i32&&) -> void> f7 {[](cpp2::i32&& x) -> void{}};
85+
std::type_identity_t<auto (*)(cpp2::out<cpp2::i32>) -> void> f8 {[](auto x) -> void{}};
86+
87+
// In alternative.
88+
cpp2::Default.expects([&] () -> bool { auto&& __expr = f0;
89+
if (cpp2::is<auto () noexcept -> void>(__expr)) { if constexpr( requires{std::terminate(), false;} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF((std::terminate(), false)),bool> ) return std::terminate(), false; else return bool{}; else return bool{}; }
90+
else if (cpp2::is<auto () -> void>(__expr)) { if constexpr( requires{std::terminate(), false;} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF((std::terminate(), false)),bool> ) return std::terminate(), false; else return bool{}; else return bool{}; }
91+
else if (cpp2::is<auto (*)() -> void>(__expr)) { if constexpr( requires{true;} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF((true)),bool> ) return true; else return bool{}; else return bool{}; }
92+
else return false; }
93+
(), "");
94+
cpp2::Default.expects([&] () -> bool { auto&& __expr = *cpp2::assert_not_null(f0);
95+
if (cpp2::is<auto () -> void>(__expr)) { if constexpr( requires{true;} ) if constexpr( std::is_convertible_v<CPP2_TYPEOF((true)),bool> ) return true; else return bool{}; else return bool{}; }
96+
else return false; }
97+
(), "");
98+
{
99+
auto (* f)() -> void = f0;
100+
101+
// As block variable.
102+
#line 58 "pure2-function-type-id.cpp2"
103+
{}
104+
}
105+
{
106+
cpp2::in<auto () -> void> f = *cpp2::assert_not_null(f0);
107+
#line 59 "pure2-function-type-id.cpp2"
108+
{ }
109+
}
110+
111+
// As local function parameter.
112+
#line 62 "pure2-function-type-id.cpp2"
113+
(void) [](auto (* f)() -> void) -> void{};
114+
(void) [](auto (* f)() -> auto (*)() -> void) -> void{};
115+
(void) [](auto (* f)() -> auto (*)() -> auto (*)() -> void) -> void{};
116+
117+
// In local function return type.
118+
(void) []() -> auto (*)() -> void { return nullptr; };
119+
(void) []() -> auto (*)() -> auto (*)() -> void { return nullptr; };
120+
121+
// Without `throws`.
122+
(void) std::type_identity_t<auto (*)(std::string_view, CPP2_MESSAGE_PARAM) noexcept -> void>{cpp2::report_and_terminate};
123+
124+
// As template argument.
125+
(void) std::type_identity_t<auto (*)() -> void>{std::move(f0)};
126+
static_assert(std::is_function_v<auto () -> void>);
127+
}
128+
129+
#line 79 "pure2-function-type-id.cpp2"
130+
auto g(auto (* f)() -> void) -> void{}
131+
auto g(auto (* f)() -> auto (*)() -> void) -> void{}
132+
133+
template<auto (* V)() -> void> auto g() -> void{}
134+
template<auto (* V)() -> auto (*)() -> void> auto g() -> void{}
135+
136+
#line 86 "pure2-function-type-id.cpp2"
137+
[[nodiscard]] auto g1() -> auto (*)() -> void { return nullptr; }
138+
[[nodiscard]] auto g2() -> auto (*)() -> auto (*)() -> void { return nullptr; }
139+
140+
#line 91 "pure2-function-type-id.cpp2"
141+
[[nodiscard]] auto f2() -> std::function<auto (cpp2::in<std::string>) -> std::string>{
142+
return [](cpp2::in<std::string> s) -> std::string{return s + " World!"; };
143+
}
144+
145+
#line 96 "pure2-function-type-id.cpp2"
146+
[[nodiscard]] auto f(cpp2::in<cpp2::i32> x) -> auto (*)(cpp2::in<cpp2::i32>) -> std::string { return +[](cpp2::in<cpp2::i32> x) -> std::string { return ""; }; }
147+
auto postfix_operators() -> void{
148+
cpp2::Default.expects(cpp2::is<auto (cpp2::in<cpp2::i32>) -> auto (*)(cpp2::in<cpp2::i32>) -> std::string>(f), "");
149+
// / | |
150+
// / | |
151+
cpp2::Default.expects(cpp2::is<auto (*)(cpp2::in<cpp2::i32>) -> std::string>(f(42)), "");
152+
// ________________/ |
153+
// / |
154+
cpp2::Default.expects(cpp2::is<auto (cpp2::in<cpp2::i32>) -> std::string>(*cpp2::assert_not_null(f(42))), "");
155+
// _______________/
156+
// /
157+
cpp2::Default.expects(cpp2::is<std::string>((*cpp2::assert_not_null(f(42)))(1)), "");
158+
} // clang-format on
159+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-function-type-id.cpp2... ok (all Cpp2, passes safety checks)
2+

0 commit comments

Comments
 (0)