Skip to content

Commit

Permalink
feat: accept function-type after type-id
Browse files Browse the repository at this point in the history
Uses the same grammar for functions, _function-type_.
Parameter identifiers must be named `_` and can't be omitted.
It's generally needed to specify the `throws` qualifier
so that it doesn't lower to a `noexcept` function type.
Some examples:
```Cpp2
main: () = {
  f0: * () throws = :()      = {};
  f6: * (move _: i32) throws = :(move x: i32) = {};
  [[assert: inspect f0 -> bool {
    is ()          = (std::terminate(), false);
    is * () throws = true;
    is _           = false;
  }]]
}

// Test case from hsutter#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]]
}
```
  • Loading branch information
JohelEGP committed Jun 24, 2023
1 parent 5a57f5e commit 0c82d8e
Show file tree
Hide file tree
Showing 21 changed files with 538 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
main: (args) = { _ = :* int = args.argc&; }
3 changes: 3 additions & 0 deletions regression-tests/pure2-function-type-id-2-error.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
main: () = {
f: * (x: i32) throws = :(_) = {};
}
3 changes: 3 additions & 0 deletions regression-tests/pure2-function-type-id-3-error.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
main: () = {
f: * () [[pre: true]] = :() = {};
}
1 change: 1 addition & 0 deletions regression-tests/pure2-function-type-id-4-error.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
main: () = { f: * () -> (x: i32); }
3 changes: 3 additions & 0 deletions regression-tests/pure2-function-type-id-5-error.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
main: () = {
f: * (_) throws = :(_) = {};
}
88 changes: 88 additions & 0 deletions regression-tests/pure2-function-type-id.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
main: () = {
postfix_operators();

// Variables with type of a mix of `*`/`const` to `() throws -> void`.
f0: * () throws = :() = {};
f1: const * () throws = f0;
f2: * const () throws = f0;
f3: const * const () throws = f0;

i: i32 = 0;
i0: * i32 = i&;
i1: const * i32 = i0;
i2: * const i32 = i0;
i3: const * const i32 = i0;

// Assert consistent '*'/'const' with non-function type variables.
static_assert((std::is_const_v<decltype(f1)>) == std::is_const_v<decltype(i1)>);
static_assert((std::is_const_v<decltype(f2)>) == std::is_const_v<decltype(i2)>);
static_assert((std::is_const_v<decltype(f3)>) == std::is_const_v<decltype(i3)>);

// Variables with various kinds of parameter.
f4: * (_: i32) throws = :(x: i32) = {};
f5: * (_: std::any) throws = :(x: std::any) = {};
f6: * (move _: i32) throws = :(move x: i32) = {};
f7: * (out _: i32) throws = :(copy x) = {};

// In alternative.
[[assert: inspect f0 -> bool {
is () = (std::terminate(), false);
is () throws = (std::terminate(), false);
is * () throws = true;
is _ = false;
}]]
[[assert: inspect f0* -> bool {
is () throws = true;
is _ = false;
}]]

// As block variable.
(f: * () throws = f0) { }

// As local function parameter.
_ = :(f: * () throws) = {};
_ = :(f: * () throws -> * () throws) = {};
_ = :(f: * () throws -> * () throws -> * () throws) = {};

// In local function return type.
_ = :() -> * () throws = nullptr;
_ = :() -> * () throws -> * () throws = nullptr;

// Without `throws`.
_ = :* (copy _: std::string_view, copy _: CPP2_MESSAGE_PARAM) = cpp2::report_and_terminate;

// As template argument.
_ = :std::type_identity_t<* () throws> = f0;
}

// As non-local function parameter.
g: (f: * () throws) = { }
g: (f: * () throws -> * () throws) = { }
// As template parameter.
g: <V: * () throws> () = { }
g: <V: * () throws -> * () throws> () = { }

// In non-local function return type.
g1: () -> * () throws = nullptr;
g2: () -> * () throws -> * () throws = nullptr;

// clang-format off
// 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]]
} // clang-format on
Empty file.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

#define CPP2_USE_MODULES Yes

//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"



//=== Cpp2 type definitions and function declarations ===========================

#line 1 "pure2-bugfix-for-multi-token-type-prvalue.cpp2"
auto main(int const argc_, char const* const* const argv_) -> int;


//=== Cpp2 function definitions =================================================

#line 1 "pure2-bugfix-for-multi-token-type-prvalue.cpp2"
auto main(int const argc_, char const* const* const argv_) -> int{
auto args = cpp2::make_args(argc_, argv_);
#line 1 "pure2-bugfix-for-multi-token-type-prvalue.cpp2"
(void) std::type_identity_t<int*>{&args.argc}; }

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-bugfix-for-multi-token-type-prvalue.cpp2... ok (all Cpp2, passes safety checks)

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pure2-function-type-id-2-error.cpp2...
pure2-function-type-id-2-error.cpp2(2,9): error: the parameter of a function type must be named '_'

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pure2-function-type-id-3-error.cpp2...
pure2-function-type-id-3-error.cpp2(2,11): error: a function type can't have contracts

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pure2-function-type-id-4-error.cpp2...
pure2-function-type-id-4-error.cpp2(1,25): error: a function type can't have an anonymous return type
pure2-function-type-id-4-error.cpp2(1,14): error: f - variable must be initialized on every branch path
==> program violates initialization safety guarantee - see previous errors

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pure2-function-type-id-5-error.cpp2...
pure2-function-type-id-5-error.cpp2(2,9): error: function type parameter must have a type

135 changes: 135 additions & 0 deletions regression-tests/test-results/pure2-function-type-id.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@

#define CPP2_USE_MODULES Yes

//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"



//=== Cpp2 type definitions and function declarations ===========================

#line 1 "pure2-function-type-id.cpp2"
auto main() -> int;


#line 58 "pure2-function-type-id.cpp2"
// As non-local function parameter.
auto g(auto (* f)() -> void) -> void;
auto g(auto (* f)() -> auto (*)() -> void) -> void;
// As template parameter.
template<auto (* V)() -> void> auto g() -> void;
template<auto (* V)() -> auto (*)() -> void> auto g() -> void;

// In non-local function return type.
[[nodiscard]] auto g1() -> auto (*)() -> void;
[[nodiscard]] auto g2() -> auto (*)() -> auto (*)() -> void;

// clang-format off
// Test case from #343.
[[nodiscard]] auto f2() -> std::function<auto (cpp2::in<std::string>) -> std::string>;


#line 75 "pure2-function-type-id.cpp2"
// Adapted from <https://github.com/hsutter/cppfront/wiki/Design-note%3A-Postfix-operators>.
[[nodiscard]] auto f(cpp2::in<cpp2::i32> x) -> auto (*)(cpp2::in<cpp2::i32>) -> std::string;
auto postfix_operators() -> void;


//=== Cpp2 function definitions =================================================

#line 1 "pure2-function-type-id.cpp2"
auto main() -> int{
postfix_operators();

// Variables with type of a mix of `*`/`const` to `() throws -> void`.
std::type_identity_t<auto (*)() -> void> f0 {[]() -> void{}};
std::type_identity_t<auto (*)() -> void> const f1 {f0};
std::type_identity_t<auto (* const)() -> void> f2 {f0};
std::type_identity_t<auto (* const)() -> void> const f3 {f0};

cpp2::i32 i {0};
cpp2::i32* i0 {&i};
cpp2::i32* const i1 {i0};
cpp2::i32 const* i2 {i0};
cpp2::i32 const* const i3 {i0};

// Assert consistent '*'/'const' with non-function type variables.
static_assert((std::is_const_v<decltype(std::move(f1))>)==std::is_const_v<decltype(std::move(i1))>);
static_assert((std::is_const_v<decltype(std::move(f2))>)==std::is_const_v<decltype(std::move(i2))>);
static_assert((std::is_const_v<decltype(std::move(f3))>)==std::is_const_v<decltype(std::move(i3))>);

// Variables with various kinds of parameter.
std::type_identity_t<auto (*)(cpp2::in<cpp2::i32>) -> void> f4 {[](cpp2::in<cpp2::i32> x) -> void{}};
std::type_identity_t<auto (*)(cpp2::in<std::any>) -> void> f5 {[](cpp2::in<std::any> x) -> void{}};
std::type_identity_t<auto (*)(cpp2::i32&&) -> void> f6 {[](cpp2::i32&& x) -> void{}};
std::type_identity_t<auto (*)(cpp2::out<cpp2::i32>) -> void> f7 {[](auto x) -> void{}};

// In alternative.
cpp2::Default.expects([&] () -> bool { auto&& __expr = f0;
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{}; }
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{}; }
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{}; }
else return false; }
(), "");
cpp2::Default.expects([&] () -> bool { auto&& __expr = *cpp2::assert_not_null(f0);
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{}; }
else return false; }
(), "");
{
auto (* f)() -> void = f0;

// As block variable.
#line 40 "pure2-function-type-id.cpp2"
{}
}

// As local function parameter.
#line 43 "pure2-function-type-id.cpp2"
(void) [](auto (* f)() -> void) -> void{};
(void) [](auto (* f)() -> auto (*)() -> void) -> void{};
(void) [](auto (* f)() -> auto (*)() -> auto (*)() -> void) -> void{};

// In local function return type.
(void) []() -> auto (*)() -> void { return nullptr; };
(void) []() -> auto (*)() -> auto (*)() -> void { return nullptr; };

// Without `throws`.
(void) std::type_identity_t<auto (*)(std::string_view, CPP2_MESSAGE_PARAM) noexcept -> void>{cpp2::report_and_terminate};

// As template argument.
(void) std::type_identity_t<auto (*)() -> void>{std::move(f0)};
}

#line 59 "pure2-function-type-id.cpp2"
auto g(auto (* f)() -> void) -> void{}
auto g(auto (* f)() -> auto (*)() -> void) -> void{}

template<auto (* V)() -> void> auto g() -> void{}
template<auto (* V)() -> auto (*)() -> void> auto g() -> void{}

#line 66 "pure2-function-type-id.cpp2"
[[nodiscard]] auto g1() -> auto (*)() -> void { return nullptr; }
[[nodiscard]] auto g2() -> auto (*)() -> auto (*)() -> void { return nullptr; }

#line 71 "pure2-function-type-id.cpp2"
[[nodiscard]] auto f2() -> std::function<auto (cpp2::in<std::string>) -> std::string>{
return [](cpp2::in<std::string> s) -> std::string{return s + " World!"; };
}

#line 76 "pure2-function-type-id.cpp2"
[[nodiscard]] auto f(cpp2::in<cpp2::i32> x) -> auto (*)(cpp2::in<cpp2::i32>) -> std::string { return +[](cpp2::in<cpp2::i32> x) -> std::string { return ""; }; }
auto postfix_operators() -> void{
cpp2::Default.expects(cpp2::is<auto (cpp2::in<cpp2::i32>) -> auto (*)(cpp2::in<cpp2::i32>) -> std::string>(f), "");
// / | |
// / | |
cpp2::Default.expects(cpp2::is<auto (*)(cpp2::in<cpp2::i32>) -> std::string>(f(42)), "");
// ________________/ |
// / |
cpp2::Default.expects(cpp2::is<auto (cpp2::in<cpp2::i32>) -> std::string>(*cpp2::assert_not_null(f(42))), "");
// _______________/
// /
cpp2::Default.expects(cpp2::is<std::string>((*cpp2::assert_not_null(f(42)))(1)), "");
} // clang-format on

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-function-type-id.cpp2... ok (all Cpp2, passes safety checks)

Loading

0 comments on commit 0c82d8e

Please sign in to comment.