diff --git a/include/cpp2util.h b/include/cpp2util.h index 85839ebcc..7d62ea20c 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 000000000..9b8b17044 --- /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 000000000..f0193c256 --- /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 000000000..c73d49800 --- /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 000000000..db87fb328 --- /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 000000000..fc3870b41 --- /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 000000000..177aaef94 --- /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 000000000..e69de29bb 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 000000000..e69de29bb 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 000000000..e69de29bb 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 000000000..e69de29bb 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 000000000..f5759d408 --- /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 000000000..5db34ad57 --- /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 000000000..35c6c3147 --- /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 000000000..1b84486b8 --- /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 000000000..bf48e2cae --- /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 000000000..6b1c02fe8 --- /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 000000000..8a4807bf7 --- /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 000000000..01a4af059 --- /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 b1e39a22e..3c7e43bd4 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 c4779e5bd..b882a323c 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 168549f79..2e2416b68 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