From e4399f08aece70895dffa7de10b5b6b3b1d19a6d Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Fri, 26 Jul 2024 13:38:34 -1000 Subject: [PATCH 1/5] Starting over on function types - initial support for function types in <> lists Should generally support std::function<> style uses Aside: I'm happy to find that C++ allows parameter names in function types, thank you WG21! I hadn't seen those used in examples so I had been expecting that cppfront would have to suppress those, but std::function< int ( std::string& param_name ) > is legal code. Sweet. This commit does not yet support pointer-to-function local variables... --- include/cpp2util.h | 12 +- regression-tests/pure2-function-typeids.cpp2 | 68 +++++++ .../pure2-function-typeids.cpp.execution | 8 + .../clang-12-c++20/run-tests-clang-12.sh | 1 + .../pure2-function-typeids.cpp.execution | 8 + .../gcc-10-c++20/run-tests-gcc-10.sh | 1 + ...mixed-bugfix-for-ufcs-non-local.cpp.output | 20 +- .../pure2-function-typeids.cpp.execution | 8 + .../gcc-14-c++2b/run-tests-gcc-14.sh | 1 + .../pure2-function-typeids.cpp.execution | 8 + .../pure2-function-typeids.cpp.output | 1 + .../run-tests-msvc-2022.bat | 1 + .../test-results/pure2-function-typeids.cpp | 108 +++++++++++ .../pure2-function-typeids.cpp2.output | 2 + source/parse.h | 175 +++++++++++++----- source/sema.h | 21 ++- source/to_cpp1.h | 133 ++++++++----- 17 files changed, 466 insertions(+), 110 deletions(-) create mode 100644 regression-tests/pure2-function-typeids.cpp2 create mode 100644 regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.output create mode 100644 regression-tests/test-results/pure2-function-typeids.cpp create mode 100644 regression-tests/test-results/pure2-function-typeids.cpp2.output diff --git a/include/cpp2util.h b/include/cpp2util.h index b724d0d914..62665049d8 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -518,15 +518,17 @@ inline std::string join(List const& list) { // // Conveniences for expressing Cpp1 references (rarely useful) // -// Note: Only needed in rare cases to take full control of matching a -// Cpp1 signature exactly. Most cases don't need this, for example -// a Cpp1 virtual function signature declaration like +// Note: Only needed in rare cases to take full control of matching an +// odd Cpp1 signature exactly. Most cases don't need this... for +// example, a Cpp1 virtual function signature declaration like // -// virtual void f(int&) const +// virtual void myfunc(int& val) const // // can already be directly overriden by a Cpp2 declaration of // -// f: (override this, inout val : int) +// myfunc: (override this, inout val: int) +// // identical to this in Cpp1 syntax: +// // void myfunc(int& val) const override // // without any need to say cpp1_ref on the int parameter. // diff --git a/regression-tests/pure2-function-typeids.cpp2 b/regression-tests/pure2-function-typeids.cpp2 new file mode 100644 index 0000000000..420254be17 --- /dev/null +++ b/regression-tests/pure2-function-typeids.cpp2 @@ -0,0 +1,68 @@ + +// --- Scaffolding + +f: () = std::cout << "hello world!\n"; + +g_in : ( s: std::string) = std::cout << "Come in, (s)$\n"; +g_inout: (inout s: std::string) = std::cout << "Come in awhile, but take some biscuits on your way out, (s)$!\n"; +g_out : (out s: std::string) = s = "Gandalf"; +g_move : (move s: std::string) = std::cout << "I hear you've moving, (s)$?\n"; + +h_out : ( s: std::string) -> std::string = { std::cout << "In (s)$ ... "; return "yohoho"; } +h_forward: (inout s: std::string) -> forward std::string = { std::cout << "Inout (s)$ ... "; return s; } + + +main: () = +{ + // --- Test basic/degenerate cases + + // Ordinary pointer to function, deduced (always worked) + pf := f&; + pf(); + + // Test std::function< void() > + ff: std::function< () -> void > = f&; + ff(); + + + // --- Tests for parameters + // Note: Not forward parameters which imply a template... + // function type-ids are for single function signatures + + fg_in : std::function< (inout s: std::string) -> void > = g_in&; + fg_inout: std::function< (inout s: std::string) -> void > = g_inout&; + fg_out : std::function< (out s: std::string) -> void > = g_out&; + fg_move : std::function< (move s: std::string) -> void > = g_move&; + + frodo: std::string = "Frodo"; + + // Test in param + fg_in(frodo); + + // Test inout + fg_inout(frodo); + + // Test out + gandalf: std::string; + fg_out(out gandalf); + std::cout << "fg_out initialized the arg to: (gandalf)$\n"; + + // Test move + fg_move(frodo); // last use, so (move frodo) is not required + + + // --- Tests for single anonymous returns + // Note: Not multiple named return values... function-type-ids + // are for Cpp1-style (single anonymous, possibly void) returns + + fh_out : std::function< ( s: std::string) -> std::string > = h_out&; + fh_forward: std::function< (inout s: std::string) -> forward std::string > = h_forward&; + + // Test out return + std::cout << "fh_out returned: (fh_out(gandalf))$\n"; + + // Test forward return + std::cout << "fh_forward returned: (fh_forward(gandalf))$\n"; + + _ = gandalf; +} diff --git a/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution new file mode 100644 index 0000000000..bdb8b72a45 --- /dev/null +++ b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution @@ -0,0 +1,8 @@ +hello world! +hello world! +Come in, Frodo +Come in awhile, but take some biscuits on your way out, Frodo! +fg_out initialized the arg to: Gandalf +I hear you've moving, Frodo? +In Gandalf ... fh_out returned: yohoho +Inout Gandalf ... fh_forward returned: Gandalf diff --git a/regression-tests/test-results/clang-12-c++20/run-tests-clang-12.sh b/regression-tests/test-results/clang-12-c++20/run-tests-clang-12.sh index d179a23909..36cd819582 100644 --- a/regression-tests/test-results/clang-12-c++20/run-tests-clang-12.sh +++ b/regression-tests/test-results/clang-12-c++20/run-tests-clang-12.sh @@ -20,5 +20,6 @@ do fi done rm -f *.obj *.exp *.lib +find . -type f -exec bash -c "[ ! -s \"{}\" ] && rm \"{}\"" \; printf "\nDone: %s .cpp tests compiled\n" "$count" printf "\n %s .cpp executables generated and run\n" "$exe_count" diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution new file mode 100644 index 0000000000..bdb8b72a45 --- /dev/null +++ b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution @@ -0,0 +1,8 @@ +hello world! +hello world! +Come in, Frodo +Come in awhile, but take some biscuits on your way out, Frodo! +fg_out initialized the arg to: Gandalf +I hear you've moving, Frodo? +In Gandalf ... fh_out returned: yohoho +Inout Gandalf ... fh_forward returned: Gandalf diff --git a/regression-tests/test-results/gcc-10-c++20/run-tests-gcc-10.sh b/regression-tests/test-results/gcc-10-c++20/run-tests-gcc-10.sh index 44b67ccd4a..098601c496 100644 --- a/regression-tests/test-results/gcc-10-c++20/run-tests-gcc-10.sh +++ b/regression-tests/test-results/gcc-10-c++20/run-tests-gcc-10.sh @@ -20,5 +20,6 @@ do fi done rm -f *.obj *.exp *.lib +find . -type f -exec bash -c "[ ! -s \"{}\" ] && rm \"{}\"" \; printf "\nDone: %s .cpp tests compiled\n" "$count" printf "\n %s .cpp executables generated and run\n" "$exe_count" diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index 4ba9bb6e19..7fc683d2c6 100644 --- a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | ~finally() noexcept { f(); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | ~finally() noexcept { f(); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | ~finally() noexcept { f(); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | ~finally() noexcept { f(); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | ~finally() noexcept { f(); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution new file mode 100644 index 0000000000..bdb8b72a45 --- /dev/null +++ b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution @@ -0,0 +1,8 @@ +hello world! +hello world! +Come in, Frodo +Come in awhile, but take some biscuits on your way out, Frodo! +fg_out initialized the arg to: Gandalf +I hear you've moving, Frodo? +In Gandalf ... fh_out returned: yohoho +Inout Gandalf ... fh_forward returned: Gandalf diff --git a/regression-tests/test-results/gcc-14-c++2b/run-tests-gcc-14.sh b/regression-tests/test-results/gcc-14-c++2b/run-tests-gcc-14.sh index 07eaef6ea5..5627774436 100644 --- a/regression-tests/test-results/gcc-14-c++2b/run-tests-gcc-14.sh +++ b/regression-tests/test-results/gcc-14-c++2b/run-tests-gcc-14.sh @@ -20,5 +20,6 @@ do fi done rm -f *.obj *.exp *.lib +find . -type f -exec bash -c "[ ! -s \"{}\" ] && rm \"{}\"" \; printf "\nDone: %s .cpp tests compiled\n" "$count" printf "\n %s .cpp executables generated and run\n" "$exe_count" diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution new file mode 100644 index 0000000000..bdb8b72a45 --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution @@ -0,0 +1,8 @@ +hello world! +hello world! +Come in, Frodo +Come in awhile, but take some biscuits on your way out, Frodo! +fg_out initialized the arg to: Gandalf +I hear you've moving, Frodo? +In Gandalf ... fh_out returned: yohoho +Inout Gandalf ... fh_forward returned: Gandalf diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.output b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.output new file mode 100644 index 0000000000..9629338025 --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.output @@ -0,0 +1 @@ +pure2-function-typeids.cpp diff --git a/regression-tests/test-results/msvc-2022-c++latest/run-tests-msvc-2022.bat b/regression-tests/test-results/msvc-2022-c++latest/run-tests-msvc-2022.bat index 270dfde121..6f002887a9 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/run-tests-msvc-2022.bat +++ b/regression-tests/test-results/msvc-2022-c++latest/run-tests-msvc-2022.bat @@ -26,6 +26,7 @@ for %%f in (*.cpp) do ( ) ) del pure2-*.obj mixed-*.obj *.exp *.lib +..\..\rm-empty-files.bat echo. echo Done: %count% .cpp tests compiled echo. diff --git a/regression-tests/test-results/pure2-function-typeids.cpp b/regression-tests/test-results/pure2-function-typeids.cpp new file mode 100644 index 0000000000..2aae418f39 --- /dev/null +++ b/regression-tests/test-results/pure2-function-typeids.cpp @@ -0,0 +1,108 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-function-typeids.cpp2" + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-function-typeids.cpp2" + +// --- Scaffolding + +#line 4 "pure2-function-typeids.cpp2" +auto f() -> void; + +auto g_in( cpp2::impl::in s) -> void; +auto g_inout(std::string& s) -> void; +auto g_out(cpp2::impl::out s) -> void; +auto g_move(std::string&& s) -> void; + +[[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string; +[[nodiscard]] auto h_forward(std::string& s) -> std::string&; + +#line 15 "pure2-function-typeids.cpp2" +auto main() -> int; + +//=== Cpp2 function definitions ================================================= + +#line 1 "pure2-function-typeids.cpp2" + +#line 4 "pure2-function-typeids.cpp2" +auto f() -> void { std::cout << "hello world!\n"; } + +#line 6 "pure2-function-typeids.cpp2" +auto g_in( cpp2::impl::in s) -> void { std::cout << "Come in, " + cpp2::to_string(s) + "\n"; } +#line 7 "pure2-function-typeids.cpp2" +auto g_inout(std::string& s) -> void { std::cout << "Come in awhile, but take some biscuits on your way out, " + cpp2::to_string(s) + "!\n"; } +#line 8 "pure2-function-typeids.cpp2" +auto g_out(cpp2::impl::out s) -> void { s.construct("Gandalf"); } +#line 9 "pure2-function-typeids.cpp2" +auto g_move(std::string&& s) -> void { std::cout << "I hear you've moving, " + cpp2::to_string(cpp2::move(s)) + "?\n"; } + +#line 11 "pure2-function-typeids.cpp2" +[[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string{std::cout << "In " + cpp2::to_string(s) + " ... ";return "yohoho"; } +#line 12 "pure2-function-typeids.cpp2" +[[nodiscard]] auto h_forward(std::string& s) -> std::string&{std::cout << "Inout " + cpp2::to_string(s) + " ... "; return s; } + +#line 15 "pure2-function-typeids.cpp2" +auto main() -> int +{ + // --- Test basic/degenerate cases + + // Ordinary pointer to function, deduced (always worked) + auto pf {&f}; + cpp2::move(pf)(); + + // Test std::function< void() > + std::function ff {&f}; + cpp2::move(ff)(); + +#line 28 "pure2-function-typeids.cpp2" + // --- Tests for parameters + // Note: Not forward parameters which imply a template... + // function type-ids are for single function signatures + + std::function fg_in {&g_in}; + std::function fg_inout {&g_inout}; + std::function s)> fg_out {&g_out}; + std::function fg_move {&g_move}; + + std::string frodo {"Frodo"}; + + // Test in param + cpp2::move(fg_in)(frodo); + + // Test inout + cpp2::move(fg_inout)(frodo); + + // Test out + cpp2::impl::deferred_init gandalf; + cpp2::move(fg_out)(cpp2::impl::out(&gandalf)); + std::cout << "fg_out initialized the arg to: " + cpp2::to_string(gandalf.value()) + "\n"; + + // Test move + cpp2::move(fg_move)(cpp2::move(frodo));// last use, so (move frodo) is not required + +#line 54 "pure2-function-typeids.cpp2" + // --- Tests for single anonymous returns + // Note: Not multiple named return values... function-type-ids + // are for Cpp1-style (single anonymous, possibly void) returns + + std::function s)> fh_out {&h_out}; + std::function fh_forward {&h_forward}; + + // Test out return + std::cout << "fh_out returned: " + cpp2::to_string(cpp2::move(fh_out)(gandalf.value())) + "\n"; + + // Test forward return + std::cout << "fh_forward returned: " + cpp2::to_string(cpp2::move(fh_forward)(gandalf.value())) + "\n"; + + static_cast(cpp2::move(gandalf.value())); +} + diff --git a/regression-tests/test-results/pure2-function-typeids.cpp2.output b/regression-tests/test-results/pure2-function-typeids.cpp2.output new file mode 100644 index 0000000000..ce9c9c2796 --- /dev/null +++ b/regression-tests/test-results/pure2-function-typeids.cpp2.output @@ -0,0 +1,2 @@ +pure2-function-typeids.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/source/parse.h b/source/parse.h index d01a7e0957..8738f7f657 100644 --- a/source/parse.h +++ b/source/parse.h @@ -1325,6 +1325,8 @@ struct qualified_id_node }; +struct function_type_node; + struct type_id_node { source_position pos; @@ -1335,16 +1337,27 @@ 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; std::unique_ptr constraint = {}; + // Out-of-line definition of the dtor is necessary due to the forward-declared + // type(s) used in a std::unique_ptr as a member + ~type_id_node(); + + auto is_function_typeid() const + -> bool + { + return id.index() == function; + } + auto is_wildcard() const -> bool { @@ -1416,6 +1429,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: @@ -1440,6 +1455,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); if (constraint) { constraint->visit(v, depth + 1); @@ -2246,15 +2262,19 @@ auto compound_statement_node::visit(auto& v, int depth) struct parameter_declaration_node { - source_position pos = {}; - passing_style pass = passing_style::in; - int ordinal = 1; + parameter_declaration_list_node const* my_list; + + source_position pos = {}; + passing_style pass = passing_style::in; + int ordinal = 1; enum class modifier { none=0, implicit, virtual_, override_, final_ }; modifier mod = modifier::none; std::unique_ptr declaration; + parameter_declaration_node(parameter_declaration_list_node const* my) : my_list{my} { } + // API // auto has_name() const @@ -2272,6 +2292,9 @@ struct parameter_declaration_node return pass; } + auto is_in_function_typeid() const + -> bool; + auto is_implicit() const -> bool { @@ -2327,11 +2350,14 @@ struct parameter_declaration_node struct parameter_declaration_list_node { - token const* open_paren = {}; - token const* close_paren = {}; + token const* open_paren = {}; + token const* close_paren = {}; + bool in_function_typeid = false; std::vector> parameters; + parameter_declaration_list_node(bool f = false) : in_function_typeid{f} { } + // API // auto ssize() const -> auto { @@ -3112,6 +3138,8 @@ struct declaration_node { return type.index() == a_function; } auto is_object () const -> bool { return type.index() == an_object; } + auto is_object_with_function_typeid() const -> bool + { return is_object() && std::get(type)->is_function_typeid(); } auto is_base_object() const -> bool { return is_object() && has_name("this"); } auto is_member_object() const -> bool @@ -3884,6 +3912,13 @@ auto parameter_declaration_node::has_name(std::string_view s) const } +auto parameter_declaration_node::is_in_function_typeid() const + -> bool +{ + return my_list && my_list->in_function_typeid; +} + + auto function_type_node::first_parameter_name() const -> std::string { @@ -3963,8 +3998,11 @@ auto function_type_node::is_defaultable() const -> bool { if ( - my_decl->has_name("operator==") - || my_decl->has_name("operator<=>") + my_decl + && ( + my_decl->has_name("operator==") + || my_decl->has_name("operator<=>") + ) ) { return true; @@ -3982,7 +4020,7 @@ auto function_type_node::is_constructor() const && (*parameters)[0]->direction() == passing_style::out ) { - assert(my_decl->has_name("operator=")); + assert(my_decl && my_decl->has_name("operator=")); return true; } return false; @@ -4022,9 +4060,9 @@ auto function_type_node::is_move() const auto function_type_node::is_swap() const -> bool { - assert (my_decl); if ( - my_decl->has_name("swap") + my_decl + && my_decl->has_name("swap") && (*parameters).ssize() == 2 && (*parameters)[1]->has_name("that") ) @@ -4101,7 +4139,8 @@ auto function_type_node::is_comparison() const -> bool { if ( - ( + my_decl + && ( my_decl->has_name("operator==") || my_decl->has_name("operator!=") || my_decl->has_name("operator<") @@ -4122,8 +4161,11 @@ auto function_type_node::is_increment_or_decrement() const -> bool { if ( - my_decl->has_name("operator++") - || my_decl->has_name("operator--") + my_decl + && ( + my_decl->has_name("operator++") + || my_decl->has_name("operator--") + ) ) { return true; @@ -4136,7 +4178,8 @@ auto function_type_node::is_compound_assignment() const -> bool { if ( - ( + my_decl + && ( my_decl->has_name("operator+=") || my_decl->has_name("operator-=") || my_decl->has_name("operator*=") @@ -4163,7 +4206,8 @@ auto function_type_node::is_assignment() const -> bool { if ( - my_decl->has_name("operator=") + my_decl + && my_decl->has_name("operator=") && (*parameters).ssize() > 1 && (*parameters)[0]->has_name("this") && (*parameters)[0]->direction() == passing_style::inout @@ -4211,7 +4255,8 @@ auto function_type_node::is_destructor() const -> bool { if ( - my_decl->has_name("operator=") + my_decl + && my_decl->has_name("operator=") && (*parameters).ssize() == 1 && (*parameters)[0]->has_name("this") && (*parameters)[0]->direction() == passing_style::move @@ -4468,6 +4513,9 @@ struct translation_unit_node }; // Definitions of out-of-line dtors for nodes with unique_ptr members of forward-declared types + +type_id_node::~type_id_node() = default; + primary_expression_node::~primary_expression_node() = default; prefix_expression_node::~prefix_expression_node() = default; @@ -4848,6 +4896,7 @@ auto pretty_print_visualize(type_id_node const& n, int indent) if (n.id.index() == type_id_node::empty) { ret += "_"; } ret += try_pretty_print_visualize(n.id, indent); ret += try_pretty_print_visualize(n.id, indent); + ret += try_pretty_print_visualize(n.id, indent); ret += try_pretty_print_visualize(n.id, indent); if (n.constraint) { @@ -5852,6 +5901,9 @@ class parser // || curr().type() == lexeme::LeftBrace ) { + // Remember current position, because we may need to backtrack + auto start_pos = pos; + bool inside_initializer = ( peek(-1) && peek(-1)->type() == lexeme::Assignment ); @@ -5859,17 +5911,23 @@ class parser auto close = close_paren_type(open_paren->type()); auto close_text = [&] () -> std::string { if (close == lexeme::RightParen) { return ")"; } return "}"; }(); next(); + auto expr_list = expression_list(open_paren, lexeme::RightParen, inside_initializer); if (!expr_list) { - error("unexpected text - ( is not followed by an expression-list"); + //error("unexpected text - ( is not followed by an expression-list"); + pos = start_pos; // backtrack return {}; } + if (curr().type() != close_paren_type(open_paren->type())) { - error("unexpected text - expression-list is not terminated by " + close_text); + //error("unexpected text - expression-list is not terminated by " + close_text); + pos = start_pos; // backtrack return {}; } + expr_list->close_paren = &curr(); next(); + if ( curr().type() != lexeme::Semicolon && curr().type() != lexeme::RightParen @@ -5882,6 +5940,7 @@ class parser n->expression_list_is_fold_expression = expr_list->is_fold_expression(); expr_list->default_initializer = is_inside_call_expr && std::empty(expr_list->expressions); + n->expr = std::move(expr_list); return n; } @@ -6530,7 +6589,7 @@ class parser && curr().type() == lexeme::Arrow ) { - error("'->' is not Cpp2 deference syntax - write '*.' instead"); + error("'->' is not Cpp2 dereference syntax - write '*.' instead"); return {}; } } @@ -6609,6 +6668,7 @@ class parser //G type-id: //G type-qualifier-seq? qualified-id is-type-constraint? //G type-qualifier-seq? unqualified-id is-type-constraint? + //G type-qualifier-seq? function-type is-type-constraint? //G //G type-qualifier-seq: //G type-qualifier @@ -6620,7 +6680,8 @@ class parser //G auto type_id( bool allow_omitting_type_name = false, - bool allow_constraint = false + bool allow_constraint = false, + bool allow_function_type = false ) -> std::unique_ptr { @@ -6657,9 +6718,19 @@ class parser n->id = std::move(id); assert (n->id.index() == type_id_node::unqualified); } + else if (std::unique_ptr id = {}; + allow_function_type + && (id = function_type({})) != nullptr + ) + { + n->pos = id->position(); + n->id = std::move(id); + assert (n->id.index() == type_id_node::function); + } else if (!allow_omitting_type_name) { return {}; } + if (curr().type() == lexeme::Multiply) { error("'T*' is not a valid Cpp2 type; use '*T' for a pointer instead", false); return {}; @@ -6826,12 +6897,17 @@ class parser auto term = template_argument{}; do { - // If it doesn't start with * or const (which can only be a type id), + // If it doesn't start with * or const or ()-> (which can only be a type id), // try parsing it as an expression if (auto e = [&]{ if ( curr().type() == lexeme::Multiply // '*' || curr() == "const" // 'const' + || ( + curr().type() == lexeme::LeftParen + && peek(1) && peek(1)->type() == lexeme::RightParen + && peek(2) && peek(2)->type() == lexeme::Arrow + ) ) { return decltype(expression()){}; @@ -6844,7 +6920,7 @@ class parser } // Else try parsing it as a type id - else if (auto i = type_id()) { + else if (auto i = type_id(false, false, true)) { term.arg = std::move(i); } @@ -7360,7 +7436,7 @@ class parser } next(2); // eat 'do' and '(' - n->parameter = parameter_declaration(false, false, false); + n->parameter = parameter_declaration(nullptr, false, false, false); if (!n->parameter) { error("'for range do (' must be followed by a parameter declaration", false, source_position{}, true); return {}; @@ -7866,10 +7942,11 @@ class parser //G 'final' //G auto parameter_declaration( - bool is_returns = false, - bool is_named = true, - bool is_template = true, - bool is_statement = false + parameter_declaration_list_node const* my_list, + bool is_returns = false, + bool is_named = true, + bool is_template = true, + bool is_statement = false ) -> std::unique_ptr { @@ -7877,7 +7954,7 @@ class parser // a parenthesized expression statement, not a statement parameter list auto start_pos = pos; - auto n = std::make_unique(); + auto n = std::make_unique(my_list); n->pass = is_returns ? passing_style::out : passing_style::in; @@ -8051,10 +8128,11 @@ class parser //G parameter-declaration-seq ',' parameter-declaration //G auto parameter_declaration_list( - bool is_returns = false, - bool is_named = true, - bool is_template = false, - bool is_statement = false + bool is_returns = false, + bool is_named = true, + bool is_template = false, + bool is_statement = false, + bool is_function_typeid = false ) -> std::unique_ptr { @@ -8075,15 +8153,15 @@ class parser return {}; } - auto n = std::make_unique(); + auto n = std::make_unique(is_function_typeid); n->open_paren = &curr(); next(); - auto param = std::make_unique(); + auto param = std::unique_ptr(); auto count = 1; - while ((param = parameter_declaration(is_returns, is_named, is_template, is_statement)) != nullptr) + while ((param = parameter_declaration(n.get(), is_returns, is_named, is_template, is_statement)) != nullptr) { param->ordinal = count; ++count; @@ -8257,7 +8335,7 @@ class parser //G contract-seq contract //G auto function_type( - declaration_node* my_decl, + declaration_node* my_decl, // if null, this is a type-id not a declaration bool is_named = true ) -> std::unique_ptr @@ -8265,7 +8343,7 @@ class parser auto n = std::make_unique( my_decl ); // Parameters - auto parameters = parameter_declaration_list(false, is_named, false); + auto parameters = parameter_declaration_list(false, is_named, false, false, my_decl == nullptr); if (!parameters) { return {}; } @@ -8292,10 +8370,12 @@ class parser } - // If we're not at a '->' or 'requires' or contract and what follows is + // If we're in an actual function declaration (not just a function type-id), + // and are not at a '->' or 'requires' or contract and what follows is // an expression, this is a ":(params) expr" shorthand function syntax if ( - curr().type() != lexeme::Arrow + my_decl + && curr().type() != lexeme::Arrow && curr() != "requires" && (curr() != "pre" && curr() != "post") ) @@ -8366,6 +8446,11 @@ class parser else if (auto returns_list = parameter_declaration_list(true, is_named)) { + if (!my_decl) { + error("a function type alias with multiple/named return values is not yet supported"); + return {}; + } + if (std::ssize(returns_list->parameters) < 1) { error("an explicit return value list cannot be empty"); return {}; @@ -8384,6 +8469,11 @@ class parser // Pre/post conditions while (auto c = contract()) { + if (!my_decl) { + error("a function type alias with contracts is not yet supported"); + return {}; + } + if ( *c->kind != "pre" && *c->kind != "post" @@ -8638,7 +8728,7 @@ class parser } // Or just a (possibly empty == deduced) type-id - else if (auto t = type_id(true, !is_template_parameter)) + else if (auto t = type_id(true, !is_template_parameter, true)) { if ( t->get_token() @@ -9682,9 +9772,10 @@ class parse_tree_printer : printing_visitor assert( n.declaration ); } - auto start(parameter_declaration_list_node const&, int indent) -> void + auto start(parameter_declaration_list_node const& n, int indent) -> void { o << pre(indent) << "parameter-declaration-list\n"; + o << pre(indent+1) << "in_function_type " << std::boolalpha << n.in_function_typeid << "\n"; } auto start(translation_unit_node const&, int indent) -> void diff --git a/source/sema.h b/source/sema.h index 27169fee00..bc4ec0d968 100644 --- a/source/sema.h +++ b/source/sema.h @@ -2069,8 +2069,11 @@ class sema // An increment/decrement function must have a single 'inout' parameter, // and if it's a member flag it if we know the type is not copyable if ( - n.my_decl->has_name("operator++") - || n.my_decl->has_name("operator--") + n.my_decl + && ( + n.my_decl->has_name("operator++") + || n.my_decl->has_name("operator--") + ) ) { if ( @@ -2302,10 +2305,12 @@ class sema inside_next_expression = false; } - auto start(parameter_declaration_list_node const&, int) -> void + auto start(parameter_declaration_list_node const& n, int) -> void { inside_parameter_list = true; - push_lifetime_scope(); + if (!n.in_function_typeid) { + push_lifetime_scope(); + } } auto end(parameter_declaration_list_node const&, int) -> void @@ -2325,6 +2330,11 @@ class sema auto start(parameter_declaration_node const& n, int) -> void { + // Ignore parameters in function type-ids + if (n.is_in_function_typeid()) { + return; + } + if ( // If it's an 'out' parameter ( @@ -2439,6 +2449,7 @@ class sema } if ( + // Skip aliases !n.is_alias() // Skip type scope (member) variables && !(n.parent_is_type() && n.is_object()) @@ -2452,6 +2463,8 @@ class sema !inside_parameter_list || inside_out_parameter ) + // Skip local variables that are pointers/etc. to functions + //&& !n.is_object_with_function_typeid() ) { symbols.emplace_back( scope_depth, declaration_sym( false, &n, nullptr, nullptr, inside_out_parameter, false, inside_returns_list ) ); diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 345283cc91..3e5223cef2 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -1906,6 +1906,7 @@ class cppfront else { try_emit(n.id, 0, false); try_emit(n.id); + try_emit(n.id); try_emit(n.id); } @@ -4808,6 +4809,63 @@ class cppfront return; } + // Common parts we may use in different orders + // + auto handle_default_return_type = [&]{ + if (is_main) + { + printer.print_cpp2( " -> int", n.position() ); + } + else if(!is_ctor_or_dtor) + { + printer.print_cpp2( " -> void", n.position() ); + } + }; + + auto handle_single_anon_return_type = [&]{ + auto is_type_scope_function_with_in_this = + n.my_decl + && n.my_decl->parent_is_type() + && n.parameters->ssize() > 0 + && (*n.parameters)[0]->direction() == passing_style::in + ; + + auto& r = std::get(n.returns); + assert(r.type); + + auto return_type = print_to_string(*r.type); + + if (r.pass == passing_style::forward) { + if (r.type->is_wildcard()) { + printer.print_cpp2( "auto&&", n.position() ); + } + else { + printer.print_cpp2( return_type, n.position() ); + if (is_type_scope_function_with_in_this) { + printer.print_cpp2( " const&", n.position() ); + } + else if (!generating_postfix_inc_dec) { + printer.print_cpp2( "&", n.position() ); + } + } + } + else { + printer.print_cpp2( return_type, n.position() ); + } + }; + + + // If this is not part of a declaration, just a function type-id, + // the return type comes first + if (!n.my_decl) { + if (n.returns.index() == function_type_node::empty) { + handle_default_return_type(); + } + else if (n.returns.index() == function_type_node::id) { + handle_single_anon_return_type(); + } + } + if ( is_main && n.parameters->parameters.size() > 0 @@ -4826,7 +4884,10 @@ class cppfront } // For an anonymous function, the emitted lambda is 'constexpr' or 'mutable' - if (!n.my_decl->has_name()) + if ( + n.my_decl + && !n.my_decl->has_name() + ) { if (n.my_decl->is_constexpr) { // The current design path we're trying out is for all '==' functions to be @@ -4851,7 +4912,10 @@ class cppfront n.is_move() || n.is_swap() || n.is_destructor() - || generating_move_from == n.my_decl + || ( + n.my_decl + && generating_move_from == n.my_decl + ) ) { printer.print_cpp2( " noexcept", n.position() ); @@ -4862,7 +4926,10 @@ class cppfront // Handle a special member function if ( n.is_assignment() - || generating_assignment_from == n.my_decl + || ( + n.my_decl + && generating_assignment_from == n.my_decl + ) ) { assert( @@ -4875,59 +4942,27 @@ class cppfront ); } - // Otherwise, handle a default return type - else if (n.returns.index() == function_type_node::empty) + // If this is a declaration, emit the trailing return type + else if (n.my_decl) { - if (is_main) - { - printer.print_cpp2( " -> int", n.position() ); - } - else if(!is_ctor_or_dtor) - { - printer.print_cpp2( " -> void", n.position() ); + // Otherwise, handle a default return type + if (n.returns.index() == function_type_node::empty) { + handle_default_return_type(); } - } - - // Otherwise, handle a single anonymous return type - else if (n.returns.index() == function_type_node::id) - { - auto is_type_scope_function_with_in_this = - n.my_decl->parent_is_type() - && n.parameters->ssize() > 0 - && (*n.parameters)[0]->direction() == passing_style::in - ; - - printer.print_cpp2( " -> ", n.position() ); - auto& r = std::get(n.returns); - assert(r.type); - - auto return_type = print_to_string(*r.type); - if (r.pass == passing_style::forward) { - if (r.type->is_wildcard()) { - printer.print_cpp2( "auto&&", n.position() ); - } - else { - printer.print_cpp2( return_type, n.position() ); - if (is_type_scope_function_with_in_this) { - printer.print_cpp2( " const&", n.position() ); - } - else if (!generating_postfix_inc_dec) { - printer.print_cpp2( "&", n.position() ); - } - } + // Otherwise, handle a single anonymous return type + else if (n.returns.index() == function_type_node::id) { + printer.print_cpp2( " -> ", n.position() ); + handle_single_anon_return_type(); } + + // Otherwise, handle multiple/named returns else { - printer.print_cpp2( return_type, n.position() ); + printer.print_cpp2( " -> ", n.position() ); + assert (n.my_decl); + printer.print_cpp2( multi_return_type_name(*n.my_decl), n.position()); } } - - // Otherwise, handle multiple/named returns - else { - printer.print_cpp2( " -> ", n.position() ); - assert (n.my_decl); - printer.print_cpp2( multi_return_type_name(*n.my_decl), n.position()); - } } From 7c3223f152c822ec3989e1348e0bbc93253c2cc0 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Fri, 26 Jul 2024 17:07:50 -1000 Subject: [PATCH 2/5] Add support for pointer-to-function variables --- regression-tests/pure2-function-typeids.cpp2 | 43 +++++++++----- .../pure2-function-typeids.cpp.execution | 10 +++- .../pure2-function-typeids.cpp.execution | 10 +++- .../pure2-function-typeids.cpp.execution | 10 +++- .../pure2-function-typeids.cpp.execution | 10 +++- .../test-results/pure2-function-typeids.cpp | 47 ++++++++++----- source/parse.h | 8 +-- source/to_cpp1.h | 59 +++++++++++++++---- 8 files changed, 143 insertions(+), 54 deletions(-) diff --git a/regression-tests/pure2-function-typeids.cpp2 b/regression-tests/pure2-function-typeids.cpp2 index 420254be17..c136ce4528 100644 --- a/regression-tests/pure2-function-typeids.cpp2 +++ b/regression-tests/pure2-function-typeids.cpp2 @@ -5,64 +5,79 @@ f: () = std::cout << "hello world!\n"; g_in : ( s: std::string) = std::cout << "Come in, (s)$\n"; g_inout: (inout s: std::string) = std::cout << "Come in awhile, but take some biscuits on your way out, (s)$!\n"; -g_out : (out s: std::string) = s = "Gandalf"; +g_out : (out s: std::string) = s = "A Powerful Mage"; g_move : (move s: std::string) = std::cout << "I hear you've moving, (s)$?\n"; -h_out : ( s: std::string) -> std::string = { std::cout << "In (s)$ ... "; return "yohoho"; } h_forward: (inout s: std::string) -> forward std::string = { std::cout << "Inout (s)$ ... "; return s; } +h_out : ( s: std::string) -> std::string = { std::cout << "In (s)$ ... "; return "yohoho"; } main: () = { // --- Test basic/degenerate cases - // Ordinary pointer to function, deduced (always worked) - pf := f&; - pf(); - // Test std::function< void() > ff: std::function< () -> void > = f&; ff(); + // Ordinary pointer to function, deduced (always worked) + pf: * () -> void = f&; + pf(); + // --- Tests for parameters // Note: Not forward parameters which imply a template... // function type-ids are for single function signatures - fg_in : std::function< (inout s: std::string) -> void > = g_in&; + fg_in : std::function< ( s: std::string) -> void > = g_in&; fg_inout: std::function< (inout s: std::string) -> void > = g_inout&; fg_out : std::function< (out s: std::string) -> void > = g_out&; fg_move : std::function< (move s: std::string) -> void > = g_move&; + pg_in : * ( s: std::string) -> void = g_in&; + pg_inout: * (inout s: std::string) -> void = g_inout&; + pg_out : * (out s: std::string) -> void = g_out&; + pg_move : * (move s: std::string) -> void = g_move&; frodo: std::string = "Frodo"; + sam : std::string = "Sam"; // Test in param fg_in(frodo); + pg_in(sam); // Test inout fg_inout(frodo); + pg_inout(sam); // Test out - gandalf: std::string; + gandalf : std::string; + galadriel: std::string; fg_out(out gandalf); - std::cout << "fg_out initialized the arg to: (gandalf)$\n"; + std::cout << "fg_out initialized gandalf to: (gandalf)$\n"; + pg_out(out galadriel); + std::cout << "pg_out initialized galadriel to: (galadriel)$\n"; + gandalf = "Gandalf"; + galadriel = "Galadriel"; // Test move fg_move(frodo); // last use, so (move frodo) is not required + pg_move(sam); // last use, so (move sam) is not required // --- Tests for single anonymous returns // Note: Not multiple named return values... function-type-ids // are for Cpp1-style (single anonymous, possibly void) returns - fh_out : std::function< ( s: std::string) -> std::string > = h_out&; fh_forward: std::function< (inout s: std::string) -> forward std::string > = h_forward&; - - // Test out return - std::cout << "fh_out returned: (fh_out(gandalf))$\n"; + fh_out : std::function< ( s: std::string) -> std::string > = h_out&; + ph_forward: * (inout s: std::string) -> forward std::string = h_forward&; + ph_out : * ( s: std::string) -> std::string = h_out&; // Test forward return std::cout << "fh_forward returned: (fh_forward(gandalf))$\n"; + std::cout << "ph_forward returned: (ph_forward(galadriel))$\n"; - _ = gandalf; + // Test out return + std::cout << "fh_out returned: (fh_out(gandalf))$\n"; + std::cout << "ph_out returned: (ph_out(galadriel))$\n"; } diff --git a/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution index bdb8b72a45..6e89a361e0 100644 --- a/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution @@ -1,8 +1,14 @@ hello world! hello world! Come in, Frodo +Come in, Sam Come in awhile, but take some biscuits on your way out, Frodo! -fg_out initialized the arg to: Gandalf +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage I hear you've moving, Frodo? -In Gandalf ... fh_out returned: yohoho +I hear you've moving, Sam? Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution index bdb8b72a45..6e89a361e0 100644 --- a/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution @@ -1,8 +1,14 @@ hello world! hello world! Come in, Frodo +Come in, Sam Come in awhile, but take some biscuits on your way out, Frodo! -fg_out initialized the arg to: Gandalf +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage I hear you've moving, Frodo? -In Gandalf ... fh_out returned: yohoho +I hear you've moving, Sam? Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho diff --git a/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution index bdb8b72a45..6e89a361e0 100644 --- a/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution @@ -1,8 +1,14 @@ hello world! hello world! Come in, Frodo +Come in, Sam Come in awhile, but take some biscuits on your way out, Frodo! -fg_out initialized the arg to: Gandalf +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage I hear you've moving, Frodo? -In Gandalf ... fh_out returned: yohoho +I hear you've moving, Sam? Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution index bdb8b72a45..6e89a361e0 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution @@ -1,8 +1,14 @@ hello world! hello world! Come in, Frodo +Come in, Sam Come in awhile, but take some biscuits on your way out, Frodo! -fg_out initialized the arg to: Gandalf +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage I hear you've moving, Frodo? -In Gandalf ... fh_out returned: yohoho +I hear you've moving, Sam? Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho diff --git a/regression-tests/test-results/pure2-function-typeids.cpp b/regression-tests/test-results/pure2-function-typeids.cpp index 2aae418f39..95dffdce1e 100644 --- a/regression-tests/test-results/pure2-function-typeids.cpp +++ b/regression-tests/test-results/pure2-function-typeids.cpp @@ -23,8 +23,8 @@ auto g_inout(std::string& s) -> void; auto g_out(cpp2::impl::out s) -> void; auto g_move(std::string&& s) -> void; -[[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string; [[nodiscard]] auto h_forward(std::string& s) -> std::string&; +[[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string; #line 15 "pure2-function-typeids.cpp2" auto main() -> int; @@ -41,68 +41,83 @@ auto g_in( cpp2::impl::in s) -> void { std::cout << "Come in, #line 7 "pure2-function-typeids.cpp2" auto g_inout(std::string& s) -> void { std::cout << "Come in awhile, but take some biscuits on your way out, " + cpp2::to_string(s) + "!\n"; } #line 8 "pure2-function-typeids.cpp2" -auto g_out(cpp2::impl::out s) -> void { s.construct("Gandalf"); } +auto g_out(cpp2::impl::out s) -> void { s.construct("A Powerful Mage"); } #line 9 "pure2-function-typeids.cpp2" auto g_move(std::string&& s) -> void { std::cout << "I hear you've moving, " + cpp2::to_string(cpp2::move(s)) + "?\n"; } #line 11 "pure2-function-typeids.cpp2" -[[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string{std::cout << "In " + cpp2::to_string(s) + " ... ";return "yohoho"; } -#line 12 "pure2-function-typeids.cpp2" [[nodiscard]] auto h_forward(std::string& s) -> std::string&{std::cout << "Inout " + cpp2::to_string(s) + " ... "; return s; } +#line 12 "pure2-function-typeids.cpp2" +[[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string{std::cout << "In " + cpp2::to_string(s) + " ... ";return "yohoho"; } #line 15 "pure2-function-typeids.cpp2" auto main() -> int { // --- Test basic/degenerate cases - // Ordinary pointer to function, deduced (always worked) - auto pf {&f}; - cpp2::move(pf)(); - // Test std::function< void() > std::function ff {&f}; cpp2::move(ff)(); + // Ordinary pointer to function, deduced (always worked) + void(*pf)() = &f; + cpp2::move(pf)(); + #line 28 "pure2-function-typeids.cpp2" // --- Tests for parameters // Note: Not forward parameters which imply a template... // function type-ids are for single function signatures - std::function fg_in {&g_in}; + std::function s)> fg_in {&g_in}; std::function fg_inout {&g_inout}; std::function s)> fg_out {&g_out}; std::function fg_move {&g_move}; + void(*pg_in)(cpp2::impl::in< std::string> s) = &g_in; + void(*pg_inout)( std::string& s) = &g_inout; + void(*pg_out)(cpp2::impl::out s) = &g_out; + void(*pg_move)( std::string&& s) = &g_move; std::string frodo {"Frodo"}; + std::string sam {"Sam"}; // Test in param cpp2::move(fg_in)(frodo); + cpp2::move(pg_in)(sam); // Test inout cpp2::move(fg_inout)(frodo); + cpp2::move(pg_inout)(sam); // Test out cpp2::impl::deferred_init gandalf; + cpp2::impl::deferred_init galadriel; cpp2::move(fg_out)(cpp2::impl::out(&gandalf)); - std::cout << "fg_out initialized the arg to: " + cpp2::to_string(gandalf.value()) + "\n"; + std::cout << "fg_out initialized gandalf to: " + cpp2::to_string(gandalf.value()) + "\n"; + cpp2::move(pg_out)(cpp2::impl::out(&galadriel)); + std::cout << "pg_out initialized galadriel to: " + cpp2::to_string(galadriel.value()) + "\n"; + gandalf.value() = "Gandalf"; + galadriel.value() = "Galadriel"; // Test move cpp2::move(fg_move)(cpp2::move(frodo));// last use, so (move frodo) is not required + cpp2::move(pg_move)(cpp2::move(sam));// last use, so (move sam) is not required -#line 54 "pure2-function-typeids.cpp2" +#line 67 "pure2-function-typeids.cpp2" // --- Tests for single anonymous returns // Note: Not multiple named return values... function-type-ids // are for Cpp1-style (single anonymous, possibly void) returns - std::function s)> fh_out {&h_out}; std::function fh_forward {&h_forward}; - - // Test out return - std::cout << "fh_out returned: " + cpp2::to_string(cpp2::move(fh_out)(gandalf.value())) + "\n"; + std::function s)> fh_out {&h_out}; + std::string&(*ph_forward)( std::string& s) = &h_forward; + std::string(*ph_out)(cpp2::impl::in s) = &h_out; // Test forward return std::cout << "fh_forward returned: " + cpp2::to_string(cpp2::move(fh_forward)(gandalf.value())) + "\n"; + std::cout << "ph_forward returned: " + cpp2::to_string(cpp2::move(ph_forward)(galadriel.value())) + "\n"; - static_cast(cpp2::move(gandalf.value())); + // Test out return + std::cout << "fh_out returned: " + cpp2::to_string(cpp2::move(fh_out)(cpp2::move(gandalf.value()))) + "\n"; + std::cout << "ph_out returned: " + cpp2::to_string(cpp2::move(ph_out)(cpp2::move(galadriel.value()))) + "\n"; } diff --git a/source/parse.h b/source/parse.h index 8738f7f657..671b239a66 100644 --- a/source/parse.h +++ b/source/parse.h @@ -1332,10 +1332,10 @@ struct type_id_node source_position pos; std::vector pc_qualifiers; - token const* address_of = {}; - token const* dereference_of = {}; - int dereference_cnt = {}; - token const* suspicious_initialization = {}; + token const* address_of = {}; + token const* dereference_of = {}; + int dereference_cnt = {}; + token const* suspicious_initialization = {}; enum active { empty=0, qualified, unqualified, function, keyword }; std::variant< diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 3e5223cef2..1504beb08a 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -1892,7 +1892,8 @@ class cppfront // auto emit( type_id_node const& n, - source_position pos = {} + source_position pos = {}, + std::string identifier = {} ) -> void { STACKINSTR @@ -1900,13 +1901,28 @@ class cppfront pos = n.position(); } + // Handle function types + if (n.is_function_typeid()) { + // If identifier is nonempty, we're doing a local variable with a (pointer to) + // function typeid, so stick in the pointers here for inside-out Cpp1 declarations + if (!identifier.empty()) { + for (auto q: n.pc_qualifiers) { + if (*q == "const") { identifier = " " + identifier; } + identifier = q->as_string_view() + identifier; + } + identifier = "(" + identifier + ")"; + } + emit( *std::get(n.id), false, false, {}, false, identifier ); + return; + } + + // Handle all other types if (n.is_wildcard()) { printer.print_cpp2("auto", pos); } else { try_emit(n.id, 0, false); try_emit(n.id); - try_emit(n.id); try_emit(n.id); } @@ -4801,7 +4817,8 @@ class cppfront bool is_main = false, bool is_ctor_or_dtor = false, std::string suffix1 = {}, - bool generating_postfix_inc_dec = false + bool generating_postfix_inc_dec = false, + std::string identifier = {} ) -> void { STACKINSTR @@ -4864,6 +4881,7 @@ class cppfront else if (n.returns.index() == function_type_node::id) { handle_single_anon_return_type(); } + printer.print_cpp2( identifier, n.position() ); } if ( @@ -6782,6 +6800,8 @@ class cppfront printer.print_cpp2( "extern ", n.position() ); } + assert(n.identifier); + // Emit "auto" for deduced types (of course) if (type->is_wildcard()) { assert(n.initializer); @@ -6807,10 +6827,10 @@ class cppfront return; } } - printer.preempt_position_push(n.position()); - emit( *type ); + printer.preempt_position_push(n.position()); + emit( *type, {}, print_to_string(*n.identifier) ); printer.preempt_position_pop(); - // one pointer is enough for now, pointer-to-function fun can be later + if ( !n.initializer && n.parent_is_function() @@ -6821,14 +6841,19 @@ class cppfront } printer.print_cpp2( " ", n.position()); - assert(n.identifier); // If this is anonymous object (named "_"), generate a unique name if (n.has_name("_")) { if (n.has_wildcard_type()) { errors.emplace_back( n.identifier->position(), - "an object can have an anonymous name or an anonymous type, but not both at the same time (rationale: if '_ := f();' were allowed to keep the returned object alive, that syntax would be dangerously close to '_ = f();' to discard the returned object, and such importantly opposite meanings deserve more than a one-character typo distance; and explicit discarding gets the nice syntax because it's likely more common)" + "an object can have an anonymous name or an anonymous type, " + "but not both at the same time (rationale: if '_ := f();' were " + "allowed to keep the returned object alive, that syntax would " + "be dangerously close to '_ = f();' to discard the returned " + "object, and such importantly opposite meanings deserve more " + "than a one-character typo distance; and explicit discarding " + "gets the nice syntax because it's likely more common)" ); return; } @@ -6850,7 +6875,7 @@ class cppfront n.identifier->position() ); } - else { + else if (!n.is_object_with_function_typeid()) { emit(*n.identifier); } @@ -6868,9 +6893,15 @@ class cppfront if (n.initializer) { printer.add_pad_in_this_line(-100); - if (type->is_concept()) { + if ( + type->is_concept() + || type->is_function_typeid() + ) + { printer.print_cpp2( " = ", n.position() ); - } else { + } + else + { printer.print_cpp2( " {", n.position() ); } @@ -6879,7 +6910,11 @@ class cppfront emit( *n.initializer, false ); pop_need_expression_list_parens(); - if (!type->is_concept()) { + if ( + !type->is_concept() + && !type->is_function_typeid() + ) + { printer.print_cpp2( "}", n.position() ); } } From fcd53f5fa28549346c8a24b6acaeac6e8d56a472 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Sat, 27 Jul 2024 06:29:51 -1000 Subject: [PATCH 3/5] Add docs examples for function typeids with std::function & *pfn --- docs/cpp2/contracts.md | 2 +- docs/cpp2/functions.md | 34 ++++++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/docs/cpp2/contracts.md b/docs/cpp2/contracts.md index a05bf948dc..fcdc21a0db 100644 --- a/docs/cpp2/contracts.md +++ b/docs/cpp2/contracts.md @@ -57,7 +57,7 @@ Contract groups are useful to enable or disable or [set custom handlers](#violat You can create new contract groups just by creating new objects that have a `.report_violation` function. The object's name is the contract group's name. The object can be at any scope: local, global, or heap. -For example, here are some ways to use contract groups of type [`cpp2::contract_group`](#violation_handlers), which is a convenient group type: +For example, here are some ways to use contract groups of type [`cpp2::contract_group`](#violation-handlers), which is a convenient group type: ``` cpp title="Using contract groups" hl_lines="1 4 6 10-12" group_a: cpp2::contract_group = (); // a global group diff --git a/docs/cpp2/functions.md b/docs/cpp2/functions.md index 5bfcd0efcf..4771f7b13f 100644 --- a/docs/cpp2/functions.md +++ b/docs/cpp2/functions.md @@ -12,7 +12,9 @@ func: ( /* no parameters */ ) = { /* empty body */ } ``` -## Parameters +## Function signatures: Parameters, returns, and using function types + +### Parameters The parameter list is a [list](common.md#lists) enclosed by `(` `)` parentheses. Each parameter is declared using the [same unified syntax](declarations.md) as used for all declarations. For example: @@ -72,7 +74,7 @@ wrap_f: ( ``` -## Return values +### Return values A function can return either of the following. The default is `#!cpp -> void`. @@ -152,7 +154,7 @@ main: () = { ``` -### Function outputs are not implicitly discardable +#### Function outputs are not implicitly discardable A function's outputs are its return values, and the "out" state of any `out` and `inout` parameters. @@ -200,9 +202,29 @@ main: () > - A function call written in Cpp2 `x.f()` member call syntax always treats a non-`#!cpp void` return type as not discardable, even if the function was written in Cpp1 syntax that did not write `[[nodiscard]]`. +### Using function types + +The same function parameter/return syntax can be used as a function type, for example to instantiate `std::function` or to declare a pointer to function variable. For example: + +``` cpp title="Using function types with std::function and *pfunc" hl_lines="4 7" +decorate_int: (i: i32) -> std::string = "--> (i)$ <--"; + +main: () = { + pf1: std::function< (i: i32) -> std::string > = decorate_int&; + std::cout << "pf1(123) returned \"(pf1(123))$\"\n"; + + pf2: * (i: i32) -> std::string = decorate_int&; + std::cout << "pf2(456) returned \"(pf2(456))$\"\n"; +} +// Prints: +// pf1 returned "--> 123 <--" +// pf2 returned "--> 456 <--" +``` + + ## Control flow -## `#!cpp if`, `#!cpp else` — Branches +### `#!cpp if`, `#!cpp else` — Branches `if` and `else` are like always in C++, except that `(` `)` parentheses around the condition are not required. Instead, `{` `}` braces around a branch body *are* required. For example: @@ -216,7 +238,7 @@ else { ``` -## `#!cpp for`, `#!cpp while`, `#!cpp do` — Loops +### `#!cpp for`, `#!cpp while`, `#!cpp do` — Loops **`#!cpp do`** and **`#!cpp while`** are like always in C++, except that `(` `)` parentheses around the condition are not required. Instead, `{` `}` braces around the loop body *are* required. @@ -296,7 +318,7 @@ Line by line: - `next i++`: The end-of-loop-iteration statement. Note `++` is always postfix in Cpp2. -### Loop names, `#!cpp break`, and `#!cpp continue` +#### Loop names, `#!cpp break`, and `#!cpp continue` Loops can be named using the usual **name `:`** syntax that introduces all names, and `#!cpp break` and `#!cpp continue` can refer to those names. For example: From 3cf405e66f705be6a04d140343e6f05b15f2e880 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Sat, 27 Jul 2024 08:18:49 -1000 Subject: [PATCH 4/5] Add support for pointer to function parameter types --- regression-tests/pure2-function-typeids.cpp2 | 9 ++++++++ .../pure2-function-typeids.cpp.execution | 2 ++ .../pure2-function-typeids.cpp.execution | 2 ++ .../pure2-function-typeids.cpp.execution | 2 ++ .../pure2-function-typeids.cpp.execution | 2 ++ .../test-results/pure2-function-typeids.cpp | 22 ++++++++++++++++--- source/to_cpp1.h | 5 +++-- 7 files changed, 39 insertions(+), 5 deletions(-) diff --git a/regression-tests/pure2-function-typeids.cpp2 b/regression-tests/pure2-function-typeids.cpp2 index c136ce4528..a0e5b66d32 100644 --- a/regression-tests/pure2-function-typeids.cpp2 +++ b/regression-tests/pure2-function-typeids.cpp2 @@ -11,6 +11,10 @@ g_move : (move s: std::string) = std::cout << "I hear you've moving, (s)$?\n"; h_forward: (inout s: std::string) -> forward std::string = { std::cout << "Inout (s)$ ... "; return s; } h_out : ( s: std::string) -> std::string = { std::cout << "In (s)$ ... "; return "yohoho"; } +f1: (a: std::function< (x:int) -> int >) -> int = a(1); +f2: (a: * (x:int) -> int ) -> int = a(2); +g : (x:int) -> int = x+42; + main: () = { @@ -80,4 +84,9 @@ main: () = // Test out return std::cout << "fh_out returned: (fh_out(gandalf))$\n"; std::cout << "ph_out returned: (ph_out(galadriel))$\n"; + + + // --- Tests for function parameters + std::cout << "(f1(g&))$\n"; + std::cout << "(f2(g&))$\n"; } diff --git a/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution index 6e89a361e0..70cc488bc5 100644 --- a/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution @@ -12,3 +12,5 @@ Inout Gandalf ... fh_forward returned: Gandalf Inout Galadriel ... ph_forward returned: Galadriel In Gandalf ... fh_out returned: yohoho In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution index 6e89a361e0..70cc488bc5 100644 --- a/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution @@ -12,3 +12,5 @@ Inout Gandalf ... fh_forward returned: Gandalf Inout Galadriel ... ph_forward returned: Galadriel In Gandalf ... fh_out returned: yohoho In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution index 6e89a361e0..70cc488bc5 100644 --- a/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution @@ -12,3 +12,5 @@ Inout Gandalf ... fh_forward returned: Gandalf Inout Galadriel ... ph_forward returned: Galadriel In Gandalf ... fh_out returned: yohoho In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution index 6e89a361e0..70cc488bc5 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution @@ -12,3 +12,5 @@ Inout Gandalf ... fh_forward returned: Gandalf Inout Galadriel ... ph_forward returned: Galadriel In Gandalf ... fh_out returned: yohoho In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/pure2-function-typeids.cpp b/regression-tests/test-results/pure2-function-typeids.cpp index 95dffdce1e..908ad10e4f 100644 --- a/regression-tests/test-results/pure2-function-typeids.cpp +++ b/regression-tests/test-results/pure2-function-typeids.cpp @@ -26,7 +26,11 @@ auto g_move(std::string&& s) -> void; [[nodiscard]] auto h_forward(std::string& s) -> std::string&; [[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string; -#line 15 "pure2-function-typeids.cpp2" +[[nodiscard]] auto f1(cpp2::impl::in x)>> a) -> int; +[[nodiscard]] auto f2(int(*a)(cpp2::impl::in x)) -> int; +[[nodiscard]] auto g (cpp2::impl::in x) -> int; + +#line 19 "pure2-function-typeids.cpp2" auto main() -> int; //=== Cpp2 function definitions ================================================= @@ -50,7 +54,14 @@ auto g_move(std::string&& s) -> void { std::cout << "I hear you've moving, " + c #line 12 "pure2-function-typeids.cpp2" [[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string{std::cout << "In " + cpp2::to_string(s) + " ... ";return "yohoho"; } +#line 14 "pure2-function-typeids.cpp2" +[[nodiscard]] auto f1(cpp2::impl::in x)>> a) -> int { return a(1); } #line 15 "pure2-function-typeids.cpp2" +[[nodiscard]] auto f2(int(*a)(cpp2::impl::in x)) -> int { return a(2); } +#line 16 "pure2-function-typeids.cpp2" +[[nodiscard]] auto g (cpp2::impl::in x) -> int { return x + 42; } + +#line 19 "pure2-function-typeids.cpp2" auto main() -> int { // --- Test basic/degenerate cases @@ -63,7 +74,7 @@ auto main() -> int void(*pf)() = &f; cpp2::move(pf)(); -#line 28 "pure2-function-typeids.cpp2" +#line 32 "pure2-function-typeids.cpp2" // --- Tests for parameters // Note: Not forward parameters which imply a template... // function type-ids are for single function signatures @@ -102,7 +113,7 @@ auto main() -> int cpp2::move(fg_move)(cpp2::move(frodo));// last use, so (move frodo) is not required cpp2::move(pg_move)(cpp2::move(sam));// last use, so (move sam) is not required -#line 67 "pure2-function-typeids.cpp2" +#line 71 "pure2-function-typeids.cpp2" // --- Tests for single anonymous returns // Note: Not multiple named return values... function-type-ids // are for Cpp1-style (single anonymous, possibly void) returns @@ -119,5 +130,10 @@ auto main() -> int // Test out return std::cout << "fh_out returned: " + cpp2::to_string(cpp2::move(fh_out)(cpp2::move(gandalf.value()))) + "\n"; std::cout << "ph_out returned: " + cpp2::to_string(cpp2::move(ph_out)(cpp2::move(galadriel.value()))) + "\n"; + +#line 89 "pure2-function-typeids.cpp2" + // --- Tests for function parameters + std::cout << "" + cpp2::to_string(f1(&g)) + "\n"; + std::cout << "" + cpp2::to_string(f2(&g)) + "\n"; } diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 1504beb08a..765b4e2be3 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -4434,7 +4434,8 @@ class cppfront //----------------------------------------------------------------------- // Else handle ordinary parameters - auto param_type = print_to_string(type_id); + assert(n.declaration->identifier); + auto param_type = print_to_string(type_id, source_position{}, print_to_string(*n.declaration->identifier)); // If there are template parameters on this function or its enclosing // type, see if this parameter's name is an unqualified-id with a @@ -4612,7 +4613,7 @@ class cppfront if (is_returns) { printer.print_extra( " " + identifier ); } - else { + else if (!n.declaration->is_object_with_function_typeid()) { printer.print_cpp2( " ", identifier_pos ); if (n.declaration->is_variadic) { From 995b0876afe537119ea2333a92cfde46efddfcb5 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Sat, 27 Jul 2024 08:45:06 -1000 Subject: [PATCH 5/5] Support function type ids in type aliases --- regression-tests/pure2-function-typeids.cpp2 | 10 ++++++++++ .../pure2-function-typeids.cpp.execution | 1 + .../pure2-function-typeids.cpp.execution | 1 + .../pure2-function-typeids.cpp.execution | 1 + .../pure2-function-typeids.cpp.execution | 1 + .../test-results/pure2-function-typeids.cpp | 18 ++++++++++++++---- source/parse.h | 2 +- 7 files changed, 29 insertions(+), 5 deletions(-) diff --git a/regression-tests/pure2-function-typeids.cpp2 b/regression-tests/pure2-function-typeids.cpp2 index a0e5b66d32..5e3112522a 100644 --- a/regression-tests/pure2-function-typeids.cpp2 +++ b/regression-tests/pure2-function-typeids.cpp2 @@ -16,6 +16,11 @@ f2: (a: * (x:int) -> int ) -> int = a(2); g : (x:int) -> int = x+42; +// --- Tests for type aliases + +A_h_forward: type == (inout s: std::string) -> forward std::string; + + main: () = { // --- Test basic/degenerate cases @@ -77,9 +82,12 @@ main: () = ph_forward: * (inout s: std::string) -> forward std::string = h_forward&; ph_out : * ( s: std::string) -> std::string = h_out&; + ph_forward2: * A_h_forward = h_forward&; + // Test forward return std::cout << "fh_forward returned: (fh_forward(gandalf))$\n"; std::cout << "ph_forward returned: (ph_forward(galadriel))$\n"; + std::cout << "ph_forward2 returned: (ph_forward2(galadriel))$\n"; // Test out return std::cout << "fh_out returned: (fh_out(gandalf))$\n"; @@ -89,4 +97,6 @@ main: () = // --- Tests for function parameters std::cout << "(f1(g&))$\n"; std::cout << "(f2(g&))$\n"; + + } diff --git a/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution index 70cc488bc5..08f16f663d 100644 --- a/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution @@ -10,6 +10,7 @@ I hear you've moving, Frodo? I hear you've moving, Sam? Inout Gandalf ... fh_forward returned: Gandalf Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel In Gandalf ... fh_out returned: yohoho In Galadriel ... ph_out returned: yohoho 43 diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution index 70cc488bc5..08f16f663d 100644 --- a/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution @@ -10,6 +10,7 @@ I hear you've moving, Frodo? I hear you've moving, Sam? Inout Gandalf ... fh_forward returned: Gandalf Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel In Gandalf ... fh_out returned: yohoho In Galadriel ... ph_out returned: yohoho 43 diff --git a/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution index 70cc488bc5..08f16f663d 100644 --- a/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution @@ -10,6 +10,7 @@ I hear you've moving, Frodo? I hear you've moving, Sam? Inout Gandalf ... fh_forward returned: Gandalf Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel In Gandalf ... fh_out returned: yohoho In Galadriel ... ph_out returned: yohoho 43 diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution index 70cc488bc5..08f16f663d 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution @@ -10,6 +10,7 @@ I hear you've moving, Frodo? I hear you've moving, Sam? Inout Gandalf ... fh_forward returned: Gandalf Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel In Gandalf ... fh_out returned: yohoho In Galadriel ... ph_out returned: yohoho 43 diff --git a/regression-tests/test-results/pure2-function-typeids.cpp b/regression-tests/test-results/pure2-function-typeids.cpp index 908ad10e4f..bec31ef690 100644 --- a/regression-tests/test-results/pure2-function-typeids.cpp +++ b/regression-tests/test-results/pure2-function-typeids.cpp @@ -31,6 +31,11 @@ auto g_move(std::string&& s) -> void; [[nodiscard]] auto g (cpp2::impl::in x) -> int; #line 19 "pure2-function-typeids.cpp2" +// --- Tests for type aliases + +using A_h_forward = std::string&(std::string& s); + +#line 24 "pure2-function-typeids.cpp2" auto main() -> int; //=== Cpp2 function definitions ================================================= @@ -61,7 +66,7 @@ auto g_move(std::string&& s) -> void { std::cout << "I hear you've moving, " + c #line 16 "pure2-function-typeids.cpp2" [[nodiscard]] auto g (cpp2::impl::in x) -> int { return x + 42; } -#line 19 "pure2-function-typeids.cpp2" +#line 24 "pure2-function-typeids.cpp2" auto main() -> int { // --- Test basic/degenerate cases @@ -74,7 +79,7 @@ auto main() -> int void(*pf)() = &f; cpp2::move(pf)(); -#line 32 "pure2-function-typeids.cpp2" +#line 37 "pure2-function-typeids.cpp2" // --- Tests for parameters // Note: Not forward parameters which imply a template... // function type-ids are for single function signatures @@ -113,7 +118,7 @@ auto main() -> int cpp2::move(fg_move)(cpp2::move(frodo));// last use, so (move frodo) is not required cpp2::move(pg_move)(cpp2::move(sam));// last use, so (move sam) is not required -#line 71 "pure2-function-typeids.cpp2" +#line 76 "pure2-function-typeids.cpp2" // --- Tests for single anonymous returns // Note: Not multiple named return values... function-type-ids // are for Cpp1-style (single anonymous, possibly void) returns @@ -123,17 +128,22 @@ auto main() -> int std::string&(*ph_forward)( std::string& s) = &h_forward; std::string(*ph_out)(cpp2::impl::in s) = &h_out; + A_h_forward* ph_forward2 {&h_forward}; + // Test forward return std::cout << "fh_forward returned: " + cpp2::to_string(cpp2::move(fh_forward)(gandalf.value())) + "\n"; std::cout << "ph_forward returned: " + cpp2::to_string(cpp2::move(ph_forward)(galadriel.value())) + "\n"; + std::cout << "ph_forward2 returned: " + cpp2::to_string(cpp2::move(ph_forward2)(galadriel.value())) + "\n"; // Test out return std::cout << "fh_out returned: " + cpp2::to_string(cpp2::move(fh_out)(cpp2::move(gandalf.value()))) + "\n"; std::cout << "ph_out returned: " + cpp2::to_string(cpp2::move(ph_out)(cpp2::move(galadriel.value()))) + "\n"; -#line 89 "pure2-function-typeids.cpp2" +#line 97 "pure2-function-typeids.cpp2" // --- Tests for function parameters std::cout << "" + cpp2::to_string(f1(&g)) + "\n"; std::cout << "" + cpp2::to_string(f2(&g)) + "\n"; + +#line 102 "pure2-function-typeids.cpp2" } diff --git a/source/parse.h b/source/parse.h index 671b239a66..287747b0df 100644 --- a/source/parse.h +++ b/source/parse.h @@ -9185,7 +9185,7 @@ class parser // Type alias if (*a->type == "type") { - auto t = type_id(); + auto t = type_id(false, false, true); if (!t) { errors.emplace_back( curr().position(),