From 66252fa3606a633f0f462ecb012eb9f32ed56eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Thu, 22 Jun 2023 22:58:23 -0400 Subject: [PATCH 1/5] 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 Cpp1-`noexcept` function types. Some examples: ```Cpp2 main: () = { f0: * () = :() = {}; f7: * (move _: i32) = :(move x: i32) = {}; [[assert: inspect f0 -> bool { is () !throws = (std::terminate(), false); is * () = true; is _ = false; }]] } // Test case from #343. f2: () -> std::function<(_: std::string) -> std::string> = { return :(s: std::string) -> std::string = { return s + " World!"; }; } // Adapted from . f: (x: i32) -> * (_: i32) -> std::string = :(x: i32) -> std::string = ""; postfix_operators: () = { [[assert: f is (_: i32) -> * (_: i32) -> std::string]] // / | | // / | | [[assert: f(42) is * (_: i32) -> std::string]] // _________/ | // / | [[assert: f(42)* is (_: i32) -> std::string]] // ________/ // / [[assert: (f(42)*)(1) is std::string]] } --- include/cpp2util.h | 25 ++- ...2-bugfix-for-multi-token-type-prvalue.cpp2 | 1 + .../pure2-function-type-id-2-error.cpp2 | 3 + .../pure2-function-type-id-3-error.cpp2 | 3 + .../pure2-function-type-id-4-error.cpp2 | 1 + .../pure2-function-type-id-5-error.cpp2 | 3 + regression-tests/pure2-function-type-id.cpp2 | 112 ++++++++++ ...for-multi-token-type-prvalue.cpp.execution | 0 ...ix-for-multi-token-type-prvalue.cpp.output | 0 .../pure2-function-type-id.cpp.execution | 0 .../gcc-13/pure2-function-type-id.cpp.output | 0 ...e2-bugfix-for-multi-token-type-prvalue.cpp | 24 +++ ...x-for-multi-token-type-prvalue.cpp2.output | 2 + ...pure2-function-type-id-2-error.cpp2.output | 3 + ...pure2-function-type-id-3-error.cpp2.output | 3 + ...pure2-function-type-id-4-error.cpp2.output | 5 + ...pure2-function-type-id-5-error.cpp2.output | 3 + .../test-results/pure2-function-type-id.cpp | 168 +++++++++++++++ .../pure2-function-type-id.cpp2.output | 2 + source/cppfront.cpp | 101 +++++++-- source/parse.h | 191 ++++++++++++++---- source/sema.h | 3 +- 22 files changed, 589 insertions(+), 64 deletions(-) create mode 100644 regression-tests/pure2-bugfix-for-multi-token-type-prvalue.cpp2 create mode 100644 regression-tests/pure2-function-type-id-2-error.cpp2 create mode 100644 regression-tests/pure2-function-type-id-3-error.cpp2 create mode 100644 regression-tests/pure2-function-type-id-4-error.cpp2 create mode 100644 regression-tests/pure2-function-type-id-5-error.cpp2 create mode 100644 regression-tests/pure2-function-type-id.cpp2 create mode 100644 regression-tests/test-results/gcc-13/pure2-bugfix-for-multi-token-type-prvalue.cpp.execution create mode 100644 regression-tests/test-results/gcc-13/pure2-bugfix-for-multi-token-type-prvalue.cpp.output create mode 100644 regression-tests/test-results/gcc-13/pure2-function-type-id.cpp.execution create mode 100644 regression-tests/test-results/gcc-13/pure2-function-type-id.cpp.output create mode 100644 regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp create mode 100644 regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp2.output create mode 100644 regression-tests/test-results/pure2-function-type-id-2-error.cpp2.output create mode 100644 regression-tests/test-results/pure2-function-type-id-3-error.cpp2.output create mode 100644 regression-tests/test-results/pure2-function-type-id-4-error.cpp2.output create mode 100644 regression-tests/test-results/pure2-function-type-id-5-error.cpp2.output create mode 100644 regression-tests/test-results/pure2-function-type-id.cpp create mode 100644 regression-tests/test-results/pure2-function-type-id.cpp2.output diff --git a/include/cpp2util.h b/include/cpp2util.h index 85839ebcc2..7d62ea20ca 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -191,7 +191,7 @@ // in our -pure-cpp2 "import std;" simulation mode... if you need this, // use mixed mode (not -pure-cpp2) and #include all the headers you need // including this one - // + // // #include #endif @@ -277,6 +277,17 @@ using __schar = signed char; // normally use i8 instead using __uchar = unsigned char; // normally use u8 instead +//----------------------------------------------------------------------- +// +// fn_t For emitted Cpp2 function types +// +//----------------------------------------------------------------------- +// +template + requires std::is_function_v +using fn_t = T; + + //----------------------------------------------------------------------- // // String: A helper workaround for passing a string literal as a @@ -466,7 +477,7 @@ template auto Typeid() -> decltype(auto) { #ifdef CPP2_NO_RTTI Type.expects( - !"'any' dynamic casting is disabled with -fno-rtti", // more likely to appear on console + !"'any' dynamic casting is disabled with -fno-rtti", // more likely to appear on console "'any' dynamic casting is disabled with -fno-rtti" // make message available to hooked handlers ); #else @@ -828,8 +839,8 @@ auto is( X const& ) -> bool { template< typename C, typename X > requires ( - ( std::is_base_of_v || - ( std::is_polymorphic_v && std::is_polymorphic_v) + ( std::is_base_of_v || + ( std::is_polymorphic_v && std::is_polymorphic_v) ) && !std::is_same_v) auto is( X const& x ) -> bool { return Dynamic_cast(&x) != nullptr; @@ -837,8 +848,8 @@ auto is( X const& x ) -> bool { template< typename C, typename X > requires ( - ( std::is_base_of_v || - ( std::is_polymorphic_v && std::is_polymorphic_v) + ( std::is_base_of_v || + ( std::is_polymorphic_v && std::is_polymorphic_v) ) && !std::is_same_v) auto is( X const* x ) -> bool { return Dynamic_cast(x) != nullptr; @@ -1426,7 +1437,7 @@ inline auto to_string(std::string const& s) -> std::string const& template inline auto to_string(T const& sv) -> std::string - requires (std::is_convertible_v + requires (std::is_convertible_v && !std::is_convertible_v) { return std::string{sv}; diff --git a/regression-tests/pure2-bugfix-for-multi-token-type-prvalue.cpp2 b/regression-tests/pure2-bugfix-for-multi-token-type-prvalue.cpp2 new file mode 100644 index 0000000000..9b8b170443 --- /dev/null +++ b/regression-tests/pure2-bugfix-for-multi-token-type-prvalue.cpp2 @@ -0,0 +1 @@ +main: (args) = { _ = :* int = args.argc&; } diff --git a/regression-tests/pure2-function-type-id-2-error.cpp2 b/regression-tests/pure2-function-type-id-2-error.cpp2 new file mode 100644 index 0000000000..f0193c2561 --- /dev/null +++ b/regression-tests/pure2-function-type-id-2-error.cpp2 @@ -0,0 +1,3 @@ +main: () = { + f: * (x: i32) throws = :(_) = {}; +} diff --git a/regression-tests/pure2-function-type-id-3-error.cpp2 b/regression-tests/pure2-function-type-id-3-error.cpp2 new file mode 100644 index 0000000000..c73d49800b --- /dev/null +++ b/regression-tests/pure2-function-type-id-3-error.cpp2 @@ -0,0 +1,3 @@ +main: () = { + f: * () [[pre: true]] = :() = {}; +} diff --git a/regression-tests/pure2-function-type-id-4-error.cpp2 b/regression-tests/pure2-function-type-id-4-error.cpp2 new file mode 100644 index 0000000000..db87fb328d --- /dev/null +++ b/regression-tests/pure2-function-type-id-4-error.cpp2 @@ -0,0 +1 @@ +main: () = { f: * () -> (x: i32); } diff --git a/regression-tests/pure2-function-type-id-5-error.cpp2 b/regression-tests/pure2-function-type-id-5-error.cpp2 new file mode 100644 index 0000000000..fc3870b41d --- /dev/null +++ b/regression-tests/pure2-function-type-id-5-error.cpp2 @@ -0,0 +1,3 @@ +main: () = { + f: * (_) throws = :(_) = {}; +} diff --git a/regression-tests/pure2-function-type-id.cpp2 b/regression-tests/pure2-function-type-id.cpp2 new file mode 100644 index 0000000000..177aaef94e --- /dev/null +++ b/regression-tests/pure2-function-type-id.cpp2 @@ -0,0 +1,112 @@ +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; + + // Uninitialized. + f4: * () throws; + f4 = f0; + + f10: * * () throws = f0&; + f11: const * * () throws = f10; + f12: * const * () throws = f10; + f13: const * const * () throws = f10; + + 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) == std::is_const_v); + static_assert((std::is_const_v) == std::is_const_v); + static_assert((std::is_const_v) == std::is_const_v); + static_assert((std::is_const_v) == std::is_const_v); + _ = f10; + _ = f11; + _ = f12; + _ = f13; + _ = i0; + _ = i1; + _ = i2; + _ = i3; + + // Variables with various kinds of parameter. + f5: * (_: i32) throws = :(x: i32) = {}; + f6: * (_: std::any) throws = :(x: std::any) = {}; + f7: * (move _: i32) throws = :(move x: i32) = {}; + f8: * (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) { } + (f: () throws = f0*) { } + + // As local function parameter. + _ = :(f: () throws) = {}; + _ = :(f: * () throws) = {}; + _ = :(f: * () throws -> * () throws) = {}; + _ = :(f: * () throws -> * () throws -> * () throws) = {}; + + // In local function return type. + _ = :() -> forward() throws = f0$*; + _ = :() -> * () 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; + static_assert(std::is_function_v<() throws>); +} + +// As non-local function parameter. +g: (f: () throws) = { } +g: (f: * () throws) = { } +g: (f: * () throws -> * () throws) = { } +// As template parameter. +g: () = { } +g: * () throws> () = { } + +// In non-local function return type. +g1: () -> * () throws = nullptr; +g1: () -> * () throws requires V = { return 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 . + 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 diff --git a/regression-tests/test-results/gcc-13/pure2-bugfix-for-multi-token-type-prvalue.cpp.execution b/regression-tests/test-results/gcc-13/pure2-bugfix-for-multi-token-type-prvalue.cpp.execution new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/gcc-13/pure2-bugfix-for-multi-token-type-prvalue.cpp.output b/regression-tests/test-results/gcc-13/pure2-bugfix-for-multi-token-type-prvalue.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/gcc-13/pure2-function-type-id.cpp.execution b/regression-tests/test-results/gcc-13/pure2-function-type-id.cpp.execution new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/gcc-13/pure2-function-type-id.cpp.output b/regression-tests/test-results/gcc-13/pure2-function-type-id.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp b/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp new file mode 100644 index 0000000000..f5759d408e --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp @@ -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{&args.argc}; } + diff --git a/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp2.output b/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp2.output new file mode 100644 index 0000000000..5db34ad57e --- /dev/null +++ b/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp2.output @@ -0,0 +1,2 @@ +pure2-bugfix-for-multi-token-type-prvalue.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/regression-tests/test-results/pure2-function-type-id-2-error.cpp2.output b/regression-tests/test-results/pure2-function-type-id-2-error.cpp2.output new file mode 100644 index 0000000000..35c6c3147b --- /dev/null +++ b/regression-tests/test-results/pure2-function-type-id-2-error.cpp2.output @@ -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 '_' + diff --git a/regression-tests/test-results/pure2-function-type-id-3-error.cpp2.output b/regression-tests/test-results/pure2-function-type-id-3-error.cpp2.output new file mode 100644 index 0000000000..1b84486b8f --- /dev/null +++ b/regression-tests/test-results/pure2-function-type-id-3-error.cpp2.output @@ -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 + diff --git a/regression-tests/test-results/pure2-function-type-id-4-error.cpp2.output b/regression-tests/test-results/pure2-function-type-id-4-error.cpp2.output new file mode 100644 index 0000000000..bf48e2caea --- /dev/null +++ b/regression-tests/test-results/pure2-function-type-id-4-error.cpp2.output @@ -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 + diff --git a/regression-tests/test-results/pure2-function-type-id-5-error.cpp2.output b/regression-tests/test-results/pure2-function-type-id-5-error.cpp2.output new file mode 100644 index 0000000000..6b1c02fe86 --- /dev/null +++ b/regression-tests/test-results/pure2-function-type-id-5-error.cpp2.output @@ -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 + diff --git a/regression-tests/test-results/pure2-function-type-id.cpp b/regression-tests/test-results/pure2-function-type-id.cpp new file mode 100644 index 0000000000..8a4807bf70 --- /dev/null +++ b/regression-tests/test-results/pure2-function-type-id.cpp @@ -0,0 +1,168 @@ + +#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 80 "pure2-function-type-id.cpp2" +// As non-local function parameter. +auto g(cpp2::in void>> f) -> void; +auto g(cpp2::fn_t void>* f) -> void; +auto g(cpp2::fn_t cpp2::fn_t void>*>* f) -> void; +// As template parameter. +template void>* V> auto g() -> void; +template cpp2::fn_t void>*>* V> auto g() -> void; + +// In non-local function return type. +[[nodiscard]] auto g1() -> cpp2::fn_t void>*; +template [[nodiscard]] auto g1() -> cpp2::fn_t void>*; +[[nodiscard]] auto g2() -> cpp2::fn_t cpp2::fn_t void>*>*; + +// clang-format off +// Test case from #343. +[[nodiscard]] auto f2() -> std::function) -> std::string>>; + + +#line 99 "pure2-function-type-id.cpp2" +// Adapted from . + [[nodiscard]] auto f(cpp2::in x) -> cpp2::fn_t) -> 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`. + cpp2::fn_t void>* f0 {[]() -> void{}}; + cpp2::fn_t void>* const f1 {f0}; + cpp2::fn_t void> const* f2 {f0}; + cpp2::fn_t void> const* const f3 {f0}; + + // Uninitialized. + cpp2::deferred_init void>*> f4; + f4.construct(f0); + + cpp2::fn_t void>** f10 {&f0}; + cpp2::fn_t void>** const f11 {f10}; + cpp2::fn_t void>* const* f12 {f10}; + cpp2::fn_t void>* const* const f13 {f10}; + + 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)==std::is_const_v); + static_assert((std::is_const_v)==std::is_const_v); + static_assert((std::is_const_v)==std::is_const_v); + static_assert((std::is_const_v)==std::is_const_v); + (void) std::move(f10); + (void) std::move(f11); + (void) std::move(f12); + (void) std::move(f13); + (void) std::move(i0); + (void) std::move(i1); + (void) std::move(i2); + (void) std::move(i3); + + // Variables with various kinds of parameter. + cpp2::fn_t) -> void>* f5 {[](cpp2::in x) -> void{}}; + cpp2::fn_t) -> void>* f6 {[](cpp2::in x) -> void{}}; + cpp2::fn_t void>* f7 {[](cpp2::i32&& x) -> void{}}; + cpp2::fn_t) -> void>* f8 {[](auto x) -> void{}}; + + // In alternative. + cpp2::Default.expects([&] () -> bool { auto&& __expr = f0; + if (cpp2::is void>>(__expr)) { if constexpr( requires{std::terminate(), false;} ) if constexpr( std::is_convertible_v ) return std::terminate(), false; else return bool{}; else return bool{}; } + else if (cpp2::is void>>(__expr)) { if constexpr( requires{std::terminate(), false;} ) if constexpr( std::is_convertible_v ) return std::terminate(), false; else return bool{}; else return bool{}; } + else if (cpp2::is void>*>(__expr)) { if constexpr( requires{true;} ) if constexpr( std::is_convertible_v ) 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 void>>(__expr)) { if constexpr( requires{true;} ) if constexpr( std::is_convertible_v ) return true; else return bool{}; else return bool{}; } + else return false; } + (), ""); +{ +cpp2::fn_t void>* f = f0; + + // As block variable. +#line 58 "pure2-function-type-id.cpp2" + {} +} +{ +cpp2::in void>> f = *cpp2::assert_not_null(f0); +#line 59 "pure2-function-type-id.cpp2" + { } +} + + // As local function parameter. +#line 62 "pure2-function-type-id.cpp2" + (void) [](cpp2::in void>> f) -> void{}; + (void) [](cpp2::fn_t void>* f) -> void{}; + (void) [](cpp2::fn_t cpp2::fn_t void>*>* f) -> void{}; + (void) [](cpp2::fn_t cpp2::fn_t cpp2::fn_t void>*>*>* f) -> void{}; + + // In local function return type. + (void) [_0 = f0]() -> cpp2::fn_t void>& { return *cpp2::assert_not_null(_0); }; + (void) []() -> cpp2::fn_t void>* { return nullptr; }; + (void) []() -> cpp2::fn_t cpp2::fn_t void>*>* { return nullptr; }; + + // Without `throws`. + (void) std::type_identity_t void>*>{cpp2::report_and_terminate}; + + // As template argument. + (void) std::type_identity_t void>*>{std::move(f0)}; + static_assert(std::is_function_v void>>); +} + +#line 81 "pure2-function-type-id.cpp2" +auto g(cpp2::in void>> f) -> void{} +auto g(cpp2::fn_t void>* f) -> void{} +auto g(cpp2::fn_t cpp2::fn_t void>*>* f) -> void{} + +template void>* V> auto g() -> void{} +template cpp2::fn_t void>*>* V> auto g() -> void{} + +#line 89 "pure2-function-type-id.cpp2" +[[nodiscard]] auto g1() -> cpp2::fn_t void>* { return nullptr; } +template [[nodiscard]] auto g1() -> cpp2::fn_t void>* +requires (V) +#line 90 "pure2-function-type-id.cpp2" +{return nullptr; } +[[nodiscard]] auto g2() -> cpp2::fn_t cpp2::fn_t void>*>* { return nullptr; } + +#line 95 "pure2-function-type-id.cpp2" +[[nodiscard]] auto f2() -> std::function) -> std::string>>{ + return [](cpp2::in s) -> std::string{return s + " World!"; }; +} + +#line 100 "pure2-function-type-id.cpp2" + [[nodiscard]] auto f(cpp2::in x) -> cpp2::fn_t) -> std::string>* { return +[](cpp2::in x) -> std::string { return ""; }; } +auto postfix_operators() -> void{ + cpp2::Default.expects(cpp2::is) -> cpp2::fn_t) -> std::string>*>>(f), ""); + // / | | + // / | | + cpp2::Default.expects(cpp2::is) -> std::string>*>(f(42)), ""); + // ________________/ | + // / | + cpp2::Default.expects(cpp2::is) -> std::string>>(*cpp2::assert_not_null(f(42))), ""); + // _______________/ + // / + cpp2::Default.expects(cpp2::is((*cpp2::assert_not_null(f(42)))(1)), ""); +} // clang-format on + diff --git a/regression-tests/test-results/pure2-function-type-id.cpp2.output b/regression-tests/test-results/pure2-function-type-id.cpp2.output new file mode 100644 index 0000000000..01a4af059d --- /dev/null +++ b/regression-tests/test-results/pure2-function-type-id.cpp2.output @@ -0,0 +1,2 @@ +pure2-function-type-id.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/source/cppfront.cpp b/source/cppfront.cpp index b1e39a22e0..3c7e43bd43 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -975,6 +975,15 @@ class positional_printer }; +template +auto stack_value(T& var, std::type_identity_t const& value) + -> auto +{ + return finally([&var, old = std::exchange(var, value)]() { + var = old; + }); +}; + //----------------------------------------------------------------------- // // cppfront: a compiler instance @@ -1771,6 +1780,7 @@ class cppfront // auto emit( type_id_node const& n, + bool anonymous_object = false, source_position pos = {} ) -> void @@ -1779,12 +1789,19 @@ class cppfront pos = n.position(); } + auto wrap_type = anonymous_object && !n.pc_qualifiers.empty(); + + // A type that has more than 1 non-nested token may need to be wrapped. + if (wrap_type) { + printer.print_cpp2("std::type_identity_t<", pos); + } if (n.is_wildcard()) { printer.print_cpp2("auto", pos); } else { try_emit(n.id, false, false); try_emit(n.id); + try_emit(n.id); try_emit(n.id); } @@ -1792,6 +1809,9 @@ class cppfront if ((**i) == "const") { printer.print_cpp2(" ", pos); } emit(**i, false, pos); } + if (wrap_type) { + printer.print_cpp2(">", pos); + } } @@ -2509,7 +2529,7 @@ class cppfront printer.add_pad_in_this_line( -5 ); - emit(*type_id); + emit(*type_id, true); printer.print_cpp2("{", decl->position()); assert(decl->initializer); @@ -3742,7 +3762,7 @@ class cppfront printer.print_extra( "{"); for (auto& param : n.parameters->parameters) { printer.print_extra( "\n"); - printer.print_extra( print_to_string(*param) ); + printer.print_extra( print_to_string(*param, false, false, true) ); } } @@ -3827,14 +3847,23 @@ class cppfront // auto emit( parameter_declaration_node const& n, - bool is_returns = false, - bool is_template_parameter = false + bool is_returns = false, + bool is_template_parameter = false, + bool is_statement_parameter = false, + bool in_function_type_id = false ) -> void { - // Can't declare functions as parameters -- only pointers to functions which are objects assert( n.declaration ); - assert( !n.declaration->is_function() ); + // Can't declare function values as parameters -- only pointers to functions which are objects + assert( + !n.declaration->is_function() + // or lowered references to function + || ( + n.pass != passing_style::out + && n.pass != passing_style::copy + ) + ); if (!check_shadowing_of_type_scope_names(*n.declaration)) { return; @@ -4058,7 +4087,7 @@ class cppfront function_requires_conditions.push_back(req); } else { - auto type_name = print_to_string( type_id ); + auto& type_name = param_type; if (is_returns) { printer.print_extra( type_name ); } @@ -4092,7 +4121,7 @@ class cppfront if (is_returns) { printer.print_extra( " " + identifier); } - else { + else if (!in_function_type_id) { printer.print_cpp2( " " + identifier, n.declaration->identifier->position()); } @@ -4112,11 +4141,12 @@ class cppfront auto emit( parameter_declaration_list_node const& n, bool is_returns = false, - bool is_template_parameter = false + bool is_template_parameter = false, + bool in_function_type_id = false ) -> void { - in_parameter_list = true; + auto stack = stack_value(in_parameter_list, true); if (is_returns) { printer.print_extra( "{ " ); @@ -4140,7 +4170,7 @@ class cppfront } prev_pos = x->position(); assert(x); - emit(*x, is_returns, is_template_parameter); + emit(*x, is_returns, is_template_parameter, false, in_function_type_id); if (!x->declaration->has_name("this")) { first = false; } @@ -4161,8 +4191,6 @@ class cppfront emit(*n.close_paren); printer.preempt_position_pop(); } - - in_parameter_list = false; } @@ -4246,9 +4274,9 @@ class cppfront auto emit( function_type_node const& n, token const* ident, - bool is_main = false, - bool is_ctor_or_dtor = false, - std::string suffix1 = {} + bool is_main = false, + bool is_ctor_or_dtor = false, + std::string suffix1 = {} ) -> void { @@ -4284,12 +4312,27 @@ class cppfront printer.print_cpp2( suffix1, n.position() ); + emit(n, function_returns_tag{}, ident, is_main, is_ctor_or_dtor, false); + } + + auto emit( + function_type_node const& n, + function_returns_tag, + token const* ident, + bool is_main, + bool is_ctor_or_dtor, + bool is_function_type_id + ) + -> void + { // Handle a special member function if ( - n.is_assignment() - || generating_assignment_from == n.my_decl - ) - { + !is_function_type_id + && ( + n.is_assignment() + || generating_assignment_from == n.my_decl + ) + ) { assert( n.returns.index() == function_type_node::empty && n.my_decl->parent_declaration->name() @@ -4317,7 +4360,8 @@ class cppfront else if (n.returns.index() == function_type_node::id) { auto is_type_scope_function_with_in_this = - n.my_decl->parent_is_type() + !is_function_type_id + && n.my_decl->parent_is_type() && n.parameters->ssize() > 0 && (*n.parameters)[0]->direction() == passing_style::in ; @@ -4357,6 +4401,19 @@ class cppfront } } + auto emit(function_type_id_node const& n) + -> void + { + assert(n.type); + printer.print_cpp2( "cpp2::fn_tposition() ); + emit(*n.type->parameters, false, false, true); + if (!n.type->throws) { + printer.print_cpp2( " noexcept", n.type->position() ); + } + emit(*n.type, function_returns_tag{}, nullptr, false, false, true); + printer.print_cpp2( ">", n.type->position() ); + } + //----------------------------------------------------------------------- // @@ -5846,7 +5903,7 @@ class cppfront // Emit "auto" for deduced types (of course) if (type->is_wildcard()) { assert(n.initializer); - emit( *type, n.position() ); + emit( *type, false, n.position() ); } // Otherwise, emit the type else { diff --git a/source/parse.h b/source/parse.h index c4779e5bd0..b882a323c2 100644 --- a/source/parse.h +++ b/source/parse.h @@ -1098,6 +1098,8 @@ struct qualified_id_node }; +struct function_type_id_node; + struct type_id_node { source_position pos; @@ -1108,11 +1110,12 @@ struct type_id_node int dereference_cnt = {}; token const* suspicious_initialization = {}; - enum active { empty=0, qualified, unqualified, keyword }; + enum active { empty=0, qualified, unqualified, function, keyword }; std::variant< std::monostate, std::unique_ptr, std::unique_ptr, + std::unique_ptr, token const* > id; @@ -1182,6 +1185,8 @@ struct type_id_node return {}; break;case unqualified: return get(id)->get_token(); + break;case function: + return {}; break;case keyword: return get(id); break;default: @@ -1206,6 +1211,7 @@ struct type_id_node } try_visit(id, v, depth); try_visit(id, v, depth); + try_visit(id, v, depth); try_visit(id, v, depth); v.end(*this, depth); } @@ -2213,6 +2219,21 @@ struct function_type_node } }; +struct function_type_id_node { + std::unique_ptr type; + + function_type_id_node(decltype(type) v) + : type{std::move(v)} + { } + + auto visit(auto& v, int depth) + -> void + { + v.start(*this, depth); + type->visit(v, depth+1); + v.end(*this, depth); + } +}; struct type_node { @@ -4829,6 +4850,7 @@ class parser //G type-id: //G type-qualifier-seq? qualified-id //G type-qualifier-seq? unqualified-id + //G type-qualifier-seq? function-type //G //G type-qualifier-seq: //G type-qualifier @@ -4838,7 +4860,10 @@ class parser //G 'const' //G '*' //G - auto type_id() + auto type_id( + bool function_is_optional = false, + bool could_be_anonymous_returns = false + ) -> std::unique_ptr { auto n = std::make_unique(); @@ -4871,9 +4896,61 @@ class parser n->id = std::move(id); assert (n->id.index() == type_id_node::unqualified); } + else if ( + auto undo_function_parse = + [&, start_pos = pos]() { + errors.clear(); + pos = start_pos; + }; + auto f = function_type_id(nullptr, true, true) + ) { + if (!errors.empty() && (function_is_optional || could_be_anonymous_returns)) { + undo_function_parse(); + return {}; + } + if (f->type->parameters) { + for (auto& p: f->type->parameters->parameters) { + assert(p->has_name()); + if (p->name()->as_string_view() != "_") { + if (could_be_anonymous_returns && n->pc_qualifiers.empty()) { + undo_function_parse(); + return {}; + } + error( + "the parameter of a function type must be named '_'", + false, + p->position() + ); + return {}; + } + } + } + if (auto l = get_if(&f->type->returns)) { + error( + "a function type can't have an anonymous return type", + false, + (*l)->position() + ); + return {}; + } + if (!f->type->contracts.empty()) { + error( + "a function type can't have contracts", + false, + f->type->contracts.front()->position() + ); + return {}; + } + n->pos = f->type->position(); + n->id = std::move(f); + assert (n->id.index() == type_id_node::function); + } else { if (!n->pc_qualifiers.empty()) { - error("'*'/'const' type qualifiers must be followed by a type name or '_' wildcard"); + error("'*'/'const' type qualifiers must be followed by a type name or '_' wildcard"); + } + else if (function_is_optional) { + undo_function_parse(); } return {}; } @@ -4935,7 +5012,7 @@ class parser term.op = &curr(); next(); - if ((term.type = type_id()) != nullptr) { + if ((term.type = type_id(true)) != nullptr) { ; } else if ((term.expr = expression()) != nullptr) { @@ -5030,21 +5107,36 @@ class parser return decltype(expression()){}; } return expression(false); // false == disallow unparenthesized relational comparisons in template args - }() - ) + }(); + e + && ( + !e->is_expression_list() + // an empty _expression-list_ + // is actually a function type + // (Cpp1 `auto () -> void`) (so far, see CWG2450) + || !e->get_expression_list()->expressions.empty() + ) + ) { term.arg = std::move(e); } - + // Else try parsing it as a type id else if (auto i = type_id()) { term.arg = std::move(i); } - + else { - break; + errors.clear(); + pos = start_pos; + if (auto i = type_id()) { + term.arg = std::move(i); + } + else { + break; + } } - + n->template_args.push_back( std::move(term) ); } // Use the lambda trick to jam in a "next" clause @@ -5598,7 +5690,7 @@ class parser n->is_as_keyword = &curr(); next(); - if (auto id = type_id()) { + if (auto id = type_id(true)) { n->type_id = std::move(id); } else if (auto e = postfix_expression()) { @@ -5975,7 +6067,8 @@ class parser bool is_returns = false, bool is_named = true, bool is_template = true, - bool is_statement = false + bool is_statement = false, + bool is_type_id = false ) -> std::unique_ptr { @@ -6131,7 +6224,7 @@ class parser error("Cpp2 is currently exploring the path of not allowing default arguments - use overloading instead", false); return {}; } - if (is_named && is_returns) { + if (is_named && (is_returns || is_type_id)) { auto tok = n->name(); assert(tok); if (tok->type() != lexeme::Identifier) { @@ -6139,8 +6232,11 @@ class parser false, tok->position()); } else if (n->declaration->has_wildcard_type()) { - error("return parameter '" + tok->to_string(true) + "' must have a type", - false, tok->position()); + auto prefix = std::string{"function type parameter"}; + if (is_returns) { + prefix = "return parameter '" + tok->to_string(true) + "'"; + } + error(prefix + " must have a type", false, tok->position()); } } return n; @@ -6158,7 +6254,8 @@ class parser bool is_returns = false, bool is_named = true, bool is_template = false, - bool is_statement = false + bool is_statement = false, + bool is_type_id = false ) -> std::unique_ptr { @@ -6186,7 +6283,7 @@ class parser auto param = std::make_unique(); auto count = 1; - while ((param = parameter_declaration(is_returns, is_named, is_template, is_statement)) != nullptr) + while ((param = parameter_declaration(is_returns, is_named, is_template, is_statement, is_type_id)) != nullptr) { param->ordinal = count; ++count; @@ -6335,14 +6432,17 @@ class parser //G auto function_type( declaration_node* my_decl, - bool is_named = true + bool is_named = true, + bool is_type_id = false ) -> std::unique_ptr { auto n = std::make_unique( my_decl ); + auto is_function_type_id = !my_decl; + // Parameters - auto parameters = parameter_declaration_list(false, is_named, false); + auto parameters = parameter_declaration_list(false, is_named, false, false, is_type_id); if (!parameters) { return {}; } @@ -6355,11 +6455,13 @@ class parser ) { if ( - n->is_move() - || n->is_swap() - || n->is_destructor() - ) - { + !is_function_type_id + && ( + n->is_move() + || n->is_swap() + || n->is_destructor() + ) + ) { error( "(experimental restriction) Cpp2 currently does not allow a move, swap, or destructor function to be designated 'throws'" ); return {}; } @@ -6395,7 +6497,7 @@ class parser error(msg + "' must be followed by a type-id"); } } - else if (auto t = type_id()) { + else if (auto t = type_id(false, true)) { if ( t->get_token() && t->get_token()->to_string(true) == "auto" @@ -6445,6 +6547,19 @@ class parser return n; } + auto function_type_id( + declaration_node* my_decl, + bool is_named = true, + bool is_type_id = false + ) + -> std::unique_ptr + { + if (auto type = function_type(my_decl, is_named, is_type_id)) { + return std::make_unique( std::move(type) ); + } + return {}; + } + auto apply_type_meta_functions( declaration_node& decl ) -> bool; @@ -6642,17 +6757,25 @@ class parser } // Or a function type, declaring a function - and tell the function whether it's in a user-defined type - else if (auto t = function_type(n.get(), named)) + else if (auto t = function_type_id(n.get(), named)) { - n->type = std::move(t); - assert (n->is_function()); + if (is_parameter) { + t->type->my_decl = nullptr; + auto type = std::make_unique(); + type->pos = t->type->position(); + type->id = std::move(t); + n->type = std::move(type); + } else { + n->type = std::move(t->type); + assert (n->is_function()); - if (!n->meta_functions.empty()) { - errors.emplace_back( - n->meta_functions.front()->position(), - "(temporary alpha limitation) metafunctions are currently not supported on functions, only on types" - ); - return {}; + if (!n->meta_functions.empty()) { + errors.emplace_back( + n->meta_functions.front()->position(), + "(temporary alpha limitation) metafunctions are currently not supported on functions, only on types" + ); + return {}; + } } } diff --git a/source/sema.h b/source/sema.h index 168549f797..2e2416b682 100644 --- a/source/sema.h +++ b/source/sema.h @@ -477,6 +477,7 @@ class sema if (is_uninitialized_decl(*sym)) { if ( sym->declaration->is_object() + && !sym->declaration->parent_is_object() && !sym->declaration->parent_is_namespace() ) { @@ -1565,7 +1566,7 @@ class sema // Skip type scope (member) variables && !(n.parent_is_type() && n.is_object()) // Skip unnamed variables - && n.identifier + && n.identifier // Skip non-out parameters && ( !inside_parameter_list From 3145cabf7dd5f8cc60fc95526288e360e9a28723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Wed, 28 Jun 2023 15:53:20 -0400 Subject: [PATCH 2/5] refactor: workaround MSVC bogus error C3537 `error C3537: you cannot cast to a type that contains 'auto'`. See not working and working. --- .../test-results/pure2-function-type-id.cpp | 104 +++++++++--------- source/cppfront.cpp | 19 ++-- 2 files changed, 64 insertions(+), 59 deletions(-) diff --git a/regression-tests/test-results/pure2-function-type-id.cpp b/regression-tests/test-results/pure2-function-type-id.cpp index 8a4807bf70..7ebe7dc572 100644 --- a/regression-tests/test-results/pure2-function-type-id.cpp +++ b/regression-tests/test-results/pure2-function-type-id.cpp @@ -16,26 +16,26 @@ auto main() -> int; #line 80 "pure2-function-type-id.cpp2" // As non-local function parameter. -auto g(cpp2::in void>> f) -> void; -auto g(cpp2::fn_t void>* f) -> void; -auto g(cpp2::fn_t cpp2::fn_t void>*>* f) -> void; +auto g(cpp2::in> f) -> void; +auto g(cpp2::fn_t* f) -> void; +auto g(cpp2::fn_t*()>* f) -> void; // As template parameter. -template void>* V> auto g() -> void; -template cpp2::fn_t void>*>* V> auto g() -> void; +template* V> auto g() -> void; +template*()>* V> auto g() -> void; // In non-local function return type. -[[nodiscard]] auto g1() -> cpp2::fn_t void>*; -template [[nodiscard]] auto g1() -> cpp2::fn_t void>*; -[[nodiscard]] auto g2() -> cpp2::fn_t cpp2::fn_t void>*>*; +[[nodiscard]] auto g1() -> cpp2::fn_t*; +template [[nodiscard]] auto g1() -> cpp2::fn_t*; +[[nodiscard]] auto g2() -> cpp2::fn_t*()>*; // clang-format off // Test case from #343. -[[nodiscard]] auto f2() -> std::function) -> std::string>>; +[[nodiscard]] auto f2() -> std::function)>>; #line 99 "pure2-function-type-id.cpp2" // Adapted from . - [[nodiscard]] auto f(cpp2::in x) -> cpp2::fn_t) -> std::string>*; + [[nodiscard]] auto f(cpp2::in x) -> cpp2::fn_t)>*; auto postfix_operators() -> void; @@ -46,19 +46,19 @@ auto main() -> int{ postfix_operators(); // Variables with type of a mix of `*`/`const` to `() throws -> void`. - cpp2::fn_t void>* f0 {[]() -> void{}}; - cpp2::fn_t void>* const f1 {f0}; - cpp2::fn_t void> const* f2 {f0}; - cpp2::fn_t void> const* const f3 {f0}; + cpp2::fn_t* f0 {[]() -> void{}}; + cpp2::fn_t* const f1 {f0}; + cpp2::fn_t const* f2 {f0}; + cpp2::fn_t const* const f3 {f0}; // Uninitialized. - cpp2::deferred_init void>*> f4; + cpp2::deferred_init*> f4; f4.construct(f0); - cpp2::fn_t void>** f10 {&f0}; - cpp2::fn_t void>** const f11 {f10}; - cpp2::fn_t void>* const* f12 {f10}; - cpp2::fn_t void>* const* const f13 {f10}; + cpp2::fn_t** f10 {&f0}; + cpp2::fn_t** const f11 {f10}; + cpp2::fn_t* const* f12 {f10}; + cpp2::fn_t* const* const f13 {f10}; cpp2::i32 i {0}; cpp2::i32* i0 {&i}; @@ -81,86 +81,86 @@ auto main() -> int{ (void) std::move(i3); // Variables with various kinds of parameter. - cpp2::fn_t) -> void>* f5 {[](cpp2::in x) -> void{}}; - cpp2::fn_t) -> void>* f6 {[](cpp2::in x) -> void{}}; - cpp2::fn_t void>* f7 {[](cpp2::i32&& x) -> void{}}; - cpp2::fn_t) -> void>* f8 {[](auto x) -> void{}}; + cpp2::fn_t)>* f5 {[](cpp2::in x) -> void{}}; + cpp2::fn_t)>* f6 {[](cpp2::in x) -> void{}}; + cpp2::fn_t* f7 {[](cpp2::i32&& x) -> void{}}; + cpp2::fn_t)>* f8 {[](auto x) -> void{}}; // In alternative. cpp2::Default.expects([&] () -> bool { auto&& __expr = f0; - if (cpp2::is void>>(__expr)) { if constexpr( requires{std::terminate(), false;} ) if constexpr( std::is_convertible_v ) return std::terminate(), false; else return bool{}; else return bool{}; } - else if (cpp2::is void>>(__expr)) { if constexpr( requires{std::terminate(), false;} ) if constexpr( std::is_convertible_v ) return std::terminate(), false; else return bool{}; else return bool{}; } - else if (cpp2::is void>*>(__expr)) { if constexpr( requires{true;} ) if constexpr( std::is_convertible_v ) return true; else return bool{}; else return bool{}; } + if (cpp2::is>(__expr)) { if constexpr( requires{std::terminate(), false;} ) if constexpr( std::is_convertible_v ) return std::terminate(), false; else return bool{}; else return bool{}; } + else if (cpp2::is>(__expr)) { if constexpr( requires{std::terminate(), false;} ) if constexpr( std::is_convertible_v ) return std::terminate(), false; else return bool{}; else return bool{}; } + else if (cpp2::is*>(__expr)) { if constexpr( requires{true;} ) if constexpr( std::is_convertible_v ) 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 void>>(__expr)) { if constexpr( requires{true;} ) if constexpr( std::is_convertible_v ) return true; else return bool{}; else return bool{}; } + if (cpp2::is>(__expr)) { if constexpr( requires{true;} ) if constexpr( std::is_convertible_v ) return true; else return bool{}; else return bool{}; } else return false; } (), ""); { -cpp2::fn_t void>* f = f0; +cpp2::fn_t* f = f0; // As block variable. #line 58 "pure2-function-type-id.cpp2" {} } { -cpp2::in void>> f = *cpp2::assert_not_null(f0); +cpp2::in> f = *cpp2::assert_not_null(f0); #line 59 "pure2-function-type-id.cpp2" { } } // As local function parameter. #line 62 "pure2-function-type-id.cpp2" - (void) [](cpp2::in void>> f) -> void{}; - (void) [](cpp2::fn_t void>* f) -> void{}; - (void) [](cpp2::fn_t cpp2::fn_t void>*>* f) -> void{}; - (void) [](cpp2::fn_t cpp2::fn_t cpp2::fn_t void>*>*>* f) -> void{}; + (void) [](cpp2::in> f) -> void{}; + (void) [](cpp2::fn_t* f) -> void{}; + (void) [](cpp2::fn_t*()>* f) -> void{}; + (void) [](cpp2::fn_t*()>*()>* f) -> void{}; // In local function return type. - (void) [_0 = f0]() -> cpp2::fn_t void>& { return *cpp2::assert_not_null(_0); }; - (void) []() -> cpp2::fn_t void>* { return nullptr; }; - (void) []() -> cpp2::fn_t cpp2::fn_t void>*>* { return nullptr; }; + (void) [_0 = f0]() -> cpp2::fn_t& { return *cpp2::assert_not_null(_0); }; + (void) []() -> cpp2::fn_t* { return nullptr; }; + (void) []() -> cpp2::fn_t*()>* { return nullptr; }; // Without `throws`. - (void) std::type_identity_t void>*>{cpp2::report_and_terminate}; + (void) std::type_identity_t*>{cpp2::report_and_terminate}; // As template argument. - (void) std::type_identity_t void>*>{std::move(f0)}; - static_assert(std::is_function_v void>>); + (void) std::type_identity_t*>{std::move(f0)}; + static_assert(std::is_function_v>); } #line 81 "pure2-function-type-id.cpp2" -auto g(cpp2::in void>> f) -> void{} -auto g(cpp2::fn_t void>* f) -> void{} -auto g(cpp2::fn_t cpp2::fn_t void>*>* f) -> void{} +auto g(cpp2::in> f) -> void{} +auto g(cpp2::fn_t* f) -> void{} +auto g(cpp2::fn_t*()>* f) -> void{} -template void>* V> auto g() -> void{} -template cpp2::fn_t void>*>* V> auto g() -> void{} +template* V> auto g() -> void{} +template*()>* V> auto g() -> void{} #line 89 "pure2-function-type-id.cpp2" -[[nodiscard]] auto g1() -> cpp2::fn_t void>* { return nullptr; } -template [[nodiscard]] auto g1() -> cpp2::fn_t void>* +[[nodiscard]] auto g1() -> cpp2::fn_t* { return nullptr; } +template [[nodiscard]] auto g1() -> cpp2::fn_t* requires (V) #line 90 "pure2-function-type-id.cpp2" {return nullptr; } -[[nodiscard]] auto g2() -> cpp2::fn_t cpp2::fn_t void>*>* { return nullptr; } +[[nodiscard]] auto g2() -> cpp2::fn_t*()>* { return nullptr; } #line 95 "pure2-function-type-id.cpp2" -[[nodiscard]] auto f2() -> std::function) -> std::string>>{ +[[nodiscard]] auto f2() -> std::function)>>{ return [](cpp2::in s) -> std::string{return s + " World!"; }; } #line 100 "pure2-function-type-id.cpp2" - [[nodiscard]] auto f(cpp2::in x) -> cpp2::fn_t) -> std::string>* { return +[](cpp2::in x) -> std::string { return ""; }; } + [[nodiscard]] auto f(cpp2::in x) -> cpp2::fn_t)>* { return +[](cpp2::in x) -> std::string { return ""; }; } auto postfix_operators() -> void{ - cpp2::Default.expects(cpp2::is) -> cpp2::fn_t) -> std::string>*>>(f), ""); + cpp2::Default.expects(cpp2::is)>*(cpp2::in)>>(f), ""); // / | | // / | | - cpp2::Default.expects(cpp2::is) -> std::string>*>(f(42)), ""); + cpp2::Default.expects(cpp2::is)>*>(f(42)), ""); // ________________/ | // / | - cpp2::Default.expects(cpp2::is) -> std::string>>(*cpp2::assert_not_null(f(42))), ""); + cpp2::Default.expects(cpp2::is)>>(*cpp2::assert_not_null(f(42))), ""); // _______________/ // / cpp2::Default.expects(cpp2::is((*cpp2::assert_not_null(f(42)))(1)), ""); diff --git a/source/cppfront.cpp b/source/cppfront.cpp index 3c7e43bd43..e8c7006425 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -4325,6 +4325,11 @@ class cppfront ) -> void { + auto return_type_introducer = std::string{" -> "}; + if (is_function_type_id) { + return_type_introducer.clear(); + } + // Handle a special member function if ( !is_function_type_id @@ -4338,7 +4343,7 @@ class cppfront && n.my_decl->parent_declaration->name() ); printer.print_cpp2( - " -> " + print_to_string( *n.my_decl->parent_declaration->name() ) + "& ", + return_type_introducer + print_to_string( *n.my_decl->parent_declaration->name() ) + "& ", n.position() ); } @@ -4348,11 +4353,11 @@ class cppfront { if (is_main) { - printer.print_cpp2( " -> int", n.position() ); + printer.print_cpp2( return_type_introducer + "int", n.position() ); } else if(!is_ctor_or_dtor) { - printer.print_cpp2( " -> void", n.position() ); + printer.print_cpp2( return_type_introducer + "void", n.position() ); } } @@ -4366,7 +4371,7 @@ class cppfront && (*n.parameters)[0]->direction() == passing_style::in ; - printer.print_cpp2( " -> ", n.position() ); + printer.print_cpp2( return_type_introducer, n.position() ); auto& r = std::get(n.returns); assert(r.type); if (r.pass == passing_style::forward) { @@ -4390,7 +4395,7 @@ class cppfront // Otherwise, handle multiple/named returns else { - printer.print_cpp2( " -> ", n.position() ); + printer.print_cpp2( return_type_introducer, n.position() ); function_return_name = {}; printer.emit_to_string(&function_return_name); assert(ident); @@ -4405,12 +4410,12 @@ class cppfront -> void { assert(n.type); - printer.print_cpp2( "cpp2::fn_tposition() ); + printer.print_cpp2( "cpp2::fn_t<", n.type->position() ); + emit(*n.type, function_returns_tag{}, nullptr, false, false, true); emit(*n.type->parameters, false, false, true); if (!n.type->throws) { printer.print_cpp2( " noexcept", n.type->position() ); } - emit(*n.type, function_returns_tag{}, nullptr, false, false, true); printer.print_cpp2( ">", n.type->position() ); } From 82b5cc9197096653150b4cf88957c1e98e776488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Sun, 20 Aug 2023 15:19:04 -0400 Subject: [PATCH 3/5] feat!: invert `throws` logic of function _type-id_ --- .../pure2-function-type-id-2-error.cpp2 | 2 +- .../pure2-function-type-id-5-error.cpp2 | 2 +- regression-tests/pure2-function-type-id.cpp2 | 104 +++++++++--------- .../test-results/pure2-function-type-id.cpp | 18 +-- source/parse.h | 31 +++++- 5 files changed, 93 insertions(+), 64 deletions(-) diff --git a/regression-tests/pure2-function-type-id-2-error.cpp2 b/regression-tests/pure2-function-type-id-2-error.cpp2 index f0193c2561..52540c522e 100644 --- a/regression-tests/pure2-function-type-id-2-error.cpp2 +++ b/regression-tests/pure2-function-type-id-2-error.cpp2 @@ -1,3 +1,3 @@ main: () = { - f: * (x: i32) throws = :(_) = {}; + f: * (x: i32) = :(_) = {}; } diff --git a/regression-tests/pure2-function-type-id-5-error.cpp2 b/regression-tests/pure2-function-type-id-5-error.cpp2 index fc3870b41d..cdde8662bc 100644 --- a/regression-tests/pure2-function-type-id-5-error.cpp2 +++ b/regression-tests/pure2-function-type-id-5-error.cpp2 @@ -1,3 +1,3 @@ main: () = { - f: * (_) throws = :(_) = {}; + f: * (_) = :(_) = {}; } diff --git a/regression-tests/pure2-function-type-id.cpp2 b/regression-tests/pure2-function-type-id.cpp2 index 177aaef94e..0699af9029 100644 --- a/regression-tests/pure2-function-type-id.cpp2 +++ b/regression-tests/pure2-function-type-id.cpp2 @@ -1,20 +1,20 @@ 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; + // Variables with type of a mix of `*`/`const` to `() -> void`. + f0: * () = :() = {}; + f1: const * () = f0; + f2: * const () = f0; + f3: const * const () = f0; // Uninitialized. - f4: * () throws; + f4: * (); f4 = f0; - f10: * * () throws = f0&; - f11: const * * () throws = f10; - f12: * const * () throws = f10; - f13: const * const * () throws = f10; + f10: * * () = f0&; + f11: const * * () = f10; + f12: * const * () = f10; + f13: const * const * () = f10; i: i32 = 0; i0: * i32 = i&; @@ -37,76 +37,76 @@ main: () = { _ = i3; // Variables with various kinds of parameter. - f5: * (_: i32) throws = :(x: i32) = {}; - f6: * (_: std::any) throws = :(x: std::any) = {}; - f7: * (move _: i32) throws = :(move x: i32) = {}; - f8: * (out _: i32) throws = :(copy x) = {}; + f5: * (_: i32) = :(x: i32) = {}; + f6: * (_: std::any) = :(x: std::any) = {}; + f7: * (move _: i32) = :(move x: i32) = {}; + f8: * (out _: i32) = :(copy x) = {}; // In alternative. [[assert: inspect f0 -> bool { - is () = (std::terminate(), false); - is () throws = (std::terminate(), false); - is * () throws = true; - is _ = false; + is () !throws = (std::terminate(), false); + is () = (std::terminate(), false); + is * () = true; + is _ = false; }]] [[assert: inspect f0* -> bool { - is () throws = true; - is _ = false; + is () = true; + is _ = false; }]] // As block variable. - (f: * () throws = f0) { } - (f: () throws = f0*) { } + (f: * () = f0) { } + (f: () = f0*) { } // As local function parameter. - _ = :(f: () throws) = {}; - _ = :(f: * () throws) = {}; - _ = :(f: * () throws -> * () throws) = {}; - _ = :(f: * () throws -> * () throws -> * () throws) = {}; + _ = :(f: ()) = {}; + _ = :(f: * ()) = {}; + _ = :(f: * () -> * ()) = {}; + _ = :(f: * () -> * () -> * ()) = {}; // In local function return type. - _ = :() -> forward() throws = f0$*; - _ = :() -> * () throws = nullptr; - _ = :() -> * () throws -> * () throws = nullptr; + _ = :() -> forward() = f0$*; + _ = :() -> * () = nullptr; + _ = :() -> * () -> * () = nullptr; - // Without `throws`. - _ = :* (copy _: std::string_view, copy _: CPP2_MESSAGE_PARAM) = cpp2::report_and_terminate; + // With `!throws`. + _ = :* (copy _: std::string_view, copy _: CPP2_MESSAGE_PARAM) !throws = cpp2::report_and_terminate; // As template argument. - _ = :std::type_identity_t<* () throws> = f0; - static_assert(std::is_function_v<() throws>); + _ = :std::type_identity_t<* ()> = f0; + static_assert(std::is_function_v<()>); } // As non-local function parameter. -g: (f: () throws) = { } -g: (f: * () throws) = { } -g: (f: * () throws -> * () throws) = { } +g: (f: ()) = { } +g: (f: * ()) = { } +g: (f: * () -> * ()) = { } // As template parameter. -g: () = { } -g: * () throws> () = { } +g: () = { } +g: * ()> () = { } // In non-local function return type. -g1: () -> * () throws = nullptr; -g1: () -> * () throws requires V = { return nullptr; } -g2: () -> * () throws -> * () throws = nullptr; +g1: () -> * () = nullptr; +g1: () -> * () requires V = { return nullptr; } +g2: () -> * () -> * () = nullptr; // clang-format off // Test case from #343. -f2: () -> std::function<(_: std::string) throws -> std::string> = { +f2: () -> std::function<(_: std::string) -> std::string> = { return :(s: std::string) -> std::string = { return s + " World!"; }; } // Adapted from . - f: (x: i32) -> * (_: i32) throws -> std::string = +:(x: i32) -> std::string = ""; + f: (x: i32) -> * (_: i32) -> 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 is (_: i32) -> * (_: i32) -> std::string]] + // / | | + // / | | + [[assert: f(42) is * (_: i32) -> std::string]] + // _________/ | + // / | + [[assert: f(42)* is (_: i32) -> std::string]] + // ________/ // / - [[assert: (f(42)*)(1) is std::string]] + [[assert: (f(42)*)(1) is std::string]] } // clang-format on diff --git a/regression-tests/test-results/pure2-function-type-id.cpp b/regression-tests/test-results/pure2-function-type-id.cpp index 7ebe7dc572..c11c1828a2 100644 --- a/regression-tests/test-results/pure2-function-type-id.cpp +++ b/regression-tests/test-results/pure2-function-type-id.cpp @@ -45,7 +45,7 @@ auto postfix_operators() -> void; auto main() -> int{ postfix_operators(); - // Variables with type of a mix of `*`/`const` to `() throws -> void`. + // Variables with type of a mix of `*`/`const` to `() -> void`. cpp2::fn_t* f0 {[]() -> void{}}; cpp2::fn_t* const f1 {f0}; cpp2::fn_t const* f2 {f0}; @@ -107,7 +107,7 @@ cpp2::fn_t* f = f0; { cpp2::in> f = *cpp2::assert_not_null(f0); #line 59 "pure2-function-type-id.cpp2" - { } + { } } // As local function parameter. @@ -122,7 +122,7 @@ cpp2::in> f = *cpp2::assert_not_null(f0); (void) []() -> cpp2::fn_t* { return nullptr; }; (void) []() -> cpp2::fn_t*()>* { return nullptr; }; - // Without `throws`. + // With `!throws`. (void) std::type_identity_t*>{cpp2::report_and_terminate}; // As template argument. @@ -152,16 +152,16 @@ requires (V) } #line 100 "pure2-function-type-id.cpp2" - [[nodiscard]] auto f(cpp2::in x) -> cpp2::fn_t)>* { return +[](cpp2::in x) -> std::string { return ""; }; } + [[nodiscard]] auto f(cpp2::in x) -> cpp2::fn_t)>* { return [](cpp2::in x) -> std::string { return ""; }; } auto postfix_operators() -> void{ cpp2::Default.expects(cpp2::is)>*(cpp2::in)>>(f), ""); - // / | | - // / | | + // / | | + // / | | cpp2::Default.expects(cpp2::is)>*>(f(42)), ""); - // ________________/ | - // / | + // _________/ | + // / | cpp2::Default.expects(cpp2::is)>>(*cpp2::assert_not_null(f(42))), ""); - // _______________/ + // ________/ // / cpp2::Default.expects(cpp2::is((*cpp2::assert_not_null(f(42)))(1)), ""); } // clang-format on diff --git a/source/parse.h b/source/parse.h index b882a323c2..8e0085d72f 100644 --- a/source/parse.h +++ b/source/parse.h @@ -6421,6 +6421,7 @@ class parser //G //G throws-specifier: //G 'throws' + //G '!' 'throws' //G //G return-list: //G '->' type-id @@ -6449,11 +6450,26 @@ class parser n->parameters = std::move(parameters); // Optional "throws" + if ( + curr().type() == lexeme::Not + && !is_type_id + ) + { + error( "'!throws' function declarations are not supported (function types are OK)." ); + return {}; + } + if ( curr().type() == lexeme::Keyword && curr() == "throws" ) { + if (is_type_id) + { + error( "expected '!' before 'throws', or no 'throws' (the default for function type-id)" ); + return {}; + } + if ( !is_function_type_id && ( @@ -6469,6 +6485,19 @@ class parser n->throws = true; next(); } + else if ( + curr().type() == lexeme::Not + && peek(1) + && *peek(1) == "throws" + ) + { + n->throws = false; + next(2); + } + else if (is_type_id) + { + n->throws = true; + } // Optional returns if (curr().type() == lexeme::Arrow) @@ -6757,7 +6786,7 @@ class parser } // Or a function type, declaring a function - and tell the function whether it's in a user-defined type - else if (auto t = function_type_id(n.get(), named)) + else if (auto t = function_type_id(n.get(), named, is_parameter)) { if (is_parameter) { t->type->my_decl = nullptr; From b0f7565b95199350d44b696eed557aa4f2bd8dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Sun, 20 Aug 2023 15:39:51 -0400 Subject: [PATCH 4/5] refactor: remove now redundant variable --- source/parse.h | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/source/parse.h b/source/parse.h index 8e0085d72f..c5fe43bc59 100644 --- a/source/parse.h +++ b/source/parse.h @@ -6440,8 +6440,6 @@ class parser { auto n = std::make_unique( my_decl ); - auto is_function_type_id = !my_decl; - // Parameters auto parameters = parameter_declaration_list(false, is_named, false, false, is_type_id); if (!parameters) { @@ -6471,13 +6469,11 @@ class parser } if ( - !is_function_type_id - && ( - n->is_move() - || n->is_swap() - || n->is_destructor() - ) - ) { + n->is_move() + || n->is_swap() + || n->is_destructor() + ) + { error( "(experimental restriction) Cpp2 currently does not allow a move, swap, or destructor function to be designated 'throws'" ); return {}; } From 6e88091330648873c2bd8fdc32c551f0bfb6aa0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Sun, 20 Aug 2023 16:23:01 -0400 Subject: [PATCH 5/5] fix: apply rebase correctly --- ...e2-bugfix-for-multi-token-type-prvalue.cpp | 6 +-- .../test-results/pure2-function-type-id.cpp | 39 ++++++++++--------- source/cppfront.cpp | 20 ++++------ source/parse.h | 17 +++++--- 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp b/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp index f5759d408e..1a02e6fd4b 100644 --- a/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp +++ b/regression-tests/test-results/pure2-bugfix-for-multi-token-type-prvalue.cpp @@ -11,14 +11,14 @@ //=== 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; +auto main(int const argc_, char** 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 main(int const argc_, char** argv_) -> int{ auto args = cpp2::make_args(argc_, argv_); #line 1 "pure2-bugfix-for-multi-token-type-prvalue.cpp2" -(void) std::type_identity_t{&args.argc}; } +static_cast(std::type_identity_t{&args.argc}); } diff --git a/regression-tests/test-results/pure2-function-type-id.cpp b/regression-tests/test-results/pure2-function-type-id.cpp index c11c1828a2..1c477314c3 100644 --- a/regression-tests/test-results/pure2-function-type-id.cpp +++ b/regression-tests/test-results/pure2-function-type-id.cpp @@ -25,7 +25,10 @@ template*()>* V> auto g() -> void; // In non-local function return type. [[nodiscard]] auto g1() -> cpp2::fn_t*; -template [[nodiscard]] auto g1() -> cpp2::fn_t*; +template [[nodiscard]] auto g1() -> cpp2::fn_t* +CPP2_REQUIRES (V) +#line 90 "pure2-function-type-id.cpp2" +; [[nodiscard]] auto g2() -> cpp2::fn_t*()>*; // clang-format off @@ -71,14 +74,14 @@ auto main() -> int{ static_assert((std::is_const_v)==std::is_const_v); static_assert((std::is_const_v)==std::is_const_v); static_assert((std::is_const_v)==std::is_const_v); - (void) std::move(f10); - (void) std::move(f11); - (void) std::move(f12); - (void) std::move(f13); - (void) std::move(i0); - (void) std::move(i1); - (void) std::move(i2); - (void) std::move(i3); + static_cast(std::move(f10)); + static_cast(std::move(f11)); + static_cast(std::move(f12)); + static_cast(std::move(f13)); + static_cast(std::move(i0)); + static_cast(std::move(i1)); + static_cast(std::move(i2)); + static_cast(std::move(i3)); // Variables with various kinds of parameter. cpp2::fn_t)>* f5 {[](cpp2::in x) -> void{}}; @@ -112,21 +115,21 @@ cpp2::in> f = *cpp2::assert_not_null(f0); // As local function parameter. #line 62 "pure2-function-type-id.cpp2" - (void) [](cpp2::in> f) -> void{}; - (void) [](cpp2::fn_t* f) -> void{}; - (void) [](cpp2::fn_t*()>* f) -> void{}; - (void) [](cpp2::fn_t*()>*()>* f) -> void{}; + static_cast([](cpp2::in> f) -> void{}); + static_cast([](cpp2::fn_t* f) -> void{}); + static_cast([](cpp2::fn_t*()>* f) -> void{}); + static_cast([](cpp2::fn_t*()>*()>* f) -> void{}); // In local function return type. - (void) [_0 = f0]() -> cpp2::fn_t& { return *cpp2::assert_not_null(_0); }; - (void) []() -> cpp2::fn_t* { return nullptr; }; - (void) []() -> cpp2::fn_t*()>* { return nullptr; }; + static_cast([_0 = f0]() -> cpp2::fn_t& { return *cpp2::assert_not_null(_0); }); + static_cast([]() -> cpp2::fn_t* { return nullptr; }); + static_cast([]() -> cpp2::fn_t*()>* { return nullptr; }); // With `!throws`. - (void) std::type_identity_t*>{cpp2::report_and_terminate}; + static_cast(std::type_identity_t*>{cpp2::report_and_terminate}); // As template argument. - (void) std::type_identity_t*>{std::move(f0)}; + static_cast(std::type_identity_t*>{std::move(f0)}); static_assert(std::is_function_v>); } diff --git a/source/cppfront.cpp b/source/cppfront.cpp index e8c7006425..ded83b417b 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -975,15 +975,6 @@ class positional_printer }; -template -auto stack_value(T& var, std::type_identity_t const& value) - -> auto -{ - return finally([&var, old = std::exchange(var, value)]() { - var = old; - }); -}; - //----------------------------------------------------------------------- // // cppfront: a compiler instance @@ -3762,7 +3753,7 @@ class cppfront printer.print_extra( "{"); for (auto& param : n.parameters->parameters) { printer.print_extra( "\n"); - printer.print_extra( print_to_string(*param, false, false, true) ); + printer.print_extra( print_to_string(*param, false, false, false) ); } } @@ -3849,7 +3840,6 @@ class cppfront parameter_declaration_node const& n, bool is_returns = false, bool is_template_parameter = false, - bool is_statement_parameter = false, bool in_function_type_id = false ) -> void @@ -4017,7 +4007,11 @@ class cppfront // First any prefix - if (identifier == "_") { + if ( + identifier == "_" + && !in_function_type_id + ) + { printer.print_cpp2( "[[maybe_unused]] ", n.position() ); identifier = "param" + std::to_string(n.ordinal); } @@ -4170,7 +4164,7 @@ class cppfront } prev_pos = x->position(); assert(x); - emit(*x, is_returns, is_template_parameter, false, in_function_type_id); + emit(*x, is_returns, is_template_parameter, in_function_type_id); if (!x->declaration->has_name("this")) { first = false; } diff --git a/source/parse.h b/source/parse.h index c5fe43bc59..2cae65b2bb 100644 --- a/source/parse.h +++ b/source/parse.h @@ -3766,6 +3766,15 @@ struct translation_unit_node }; +template +auto stack_value(T& var, std::type_identity_t const& value) + -> auto +{ + return finally([&var, old = std::exchange(var, value)]() { + var = old; + }); +}; + //----------------------------------------------------------------------- // // parser: parses a section of Cpp2 code @@ -5096,6 +5105,7 @@ class parser auto term = unqualified_id_node::term{}; do { + auto stack = stack_value(start_pos, pos); // If it doesn't start with * or const (which can only be a type id), // try parsing it as an expression if (auto e = [&]{ @@ -5121,14 +5131,11 @@ class parser term.arg = std::move(e); } - // Else try parsing it as a type id - else if (auto i = type_id()) { - term.arg = std::move(i); - } - else { errors.clear(); pos = start_pos; + + // Else try parsing it as a type id if (auto i = type_id()) { term.arg = std::move(i); }