From b3f3f7073efcf1c8a578618c70350ffe55287ba9 Mon Sep 17 00:00:00 2001 From: Filip Sajdak Date: Mon, 26 Sep 2022 01:33:56 +0200 Subject: [PATCH 1/3] Added: UFCS chaining starts on function call --- source/cppfront.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/source/cppfront.cpp b/source/cppfront.cpp index 523613a50..1f47dda9d 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -1733,6 +1733,74 @@ class cppfront return; } + // Check to see if it's just a function call syntax chained with other functions, + // and if so use this path to convert it to UFCS + if (// there's a single-token expression followed by (, ., and ( + n.expr->get_token() && // if the base expression is a single token + std::ssize(n.ops) >= 3 && // and we're of the form: + n.ops[0].op->type() == lexeme::LeftParen && // token ( expr-list ) . id-expr ( expr-list ) + n.ops[1].op->type() == lexeme::Dot && + n.ops[2].op->type() == lexeme::LeftParen + ) + { + // If we already replaced this with a capture (which contains the UFCS + // work already done when the capture was computed), emit the capture + if (!captured_part.empty()) { + printer.print_cpp2(captured_part, n.position()); + return; + } + + // Otherwise, do the UFCS work... + + // If method are chained we need to go from the last to the first + // token(a-expr-list).b(b-expr-list).c(c-expr-list) will be tranformed to: + // CPP2_UFCS(c, CPP2_UFCS(b, CPP2_UFCS(token, a-expr-list), b-expr-list), c-expr-list ) + for (auto i = std::ssize(n.ops)-1; i > 1; i -= 2) + { + // The . has its id_expr + assert (n.ops[i-1].id_expr); + + // The ( has its expr_list and op_close + assert (n.ops[i].expr_list && n.ops[i].op_close); + + //-------------------------------------------------------------------- + // TODO: When MSVC supports __VA_OPT__ in standard mode without the + // experimental /Zc:preprocessor switch, use this single line + // instead of the dual lines below that special-case _0 args + // AND: Make the similarly noted change in cpp2util.h + // + // If there are no additional arguments, use the CPP2_UFCS_0 version + if (!n.ops[i].expr_list->expressions.empty()) { + printer.print_cpp2("CPP2_UFCS(", n.position()); + } + else { + printer.print_cpp2("CPP2_UFCS_0(", n.position()); + } + emit(*n.ops[i-1].id_expr); + printer.print_cpp2(", ", n.position()); + } + + // emit the first function that starts chaining (the most nested one) + emit(*n.expr); + if (!n.ops[0].expr_list->expressions.empty()) { + emit(*n.ops[0].expr_list); + } + + // unroll the nested calls (skipping the first function call) + // expr-list need to be added in reversed order then CPP2_UFCS macros + for (auto i = 2; i < std::ssize(n.ops); i += 2) { + // Then tack on any additional arguments + if (!n.ops[i].expr_list->expressions.empty()) { + printer.print_cpp2(", ", n.position()); + emit(*n.ops[i].expr_list); + } + printer.print_cpp2(")", n.position()); + } + + // And we're done. This path has handled this node, so return... + return; + } + // Otherwise, we're going to have to potentially do some work to change // some Cpp2 postfix operators to Cpp1 prefix operators, so let's set up... auto prefix = std::vector{}; From e75657f082a46f2bb3f508e1e459bb8b2c72ba68 Mon Sep 17 00:00:00 2001 From: Filip Sajdak Date: Mon, 26 Sep 2022 01:39:22 +0200 Subject: [PATCH 2/3] Added example of chaining of function call --- ...ed-ufcs-function-call-starts-chaining.cpp2 | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 regression-tests/mixed-ufcs-function-call-starts-chaining.cpp2 diff --git a/regression-tests/mixed-ufcs-function-call-starts-chaining.cpp2 b/regression-tests/mixed-ufcs-function-call-starts-chaining.cpp2 new file mode 100644 index 000000000..78a43ee86 --- /dev/null +++ b/regression-tests/mixed-ufcs-function-call-starts-chaining.cpp2 @@ -0,0 +1,48 @@ +#include +#include + +template +struct scope_exit { + scope_exit(T v, D d) + : value(v) + , deleter(d) + { + } + + ~scope_exit() { + deleter(value); + } + + operator T&() { return value; } + + scope_exit(const scope_exit&) = delete; + scope_exit& operator=(const scope_exit&) = delete; + scope_exit(scope_exit&& rhs) noexcept : value(std::move(rhs.value)), deleter(std::move(rhs.deleter)) {} + scope_exit& operator=(scope_exit&& rhs) noexcept { + value = std::move(rhs.value); + deleter = std::move(rhs.deleter); + return *this; + } + +private: + T value; + D deleter; +}; + +template +auto on_scope_exit(T&& v, D&& d) { + return scope_exit(std::forward(v),std::forward(d)); +} + +main: () -> int = { + + fopen("variable2.txt", "w").on_scope_exit(:(e:_) = { + e.fprintf("you can handle smart_ptrs without changing behaviour of UFCS"); + e.fclose(); + }); + + m := fopen("manual.txt", "w"); + m.fprintf("Manual handling still works"); + m.fclose(); + +} From ef5f875f921149cb7f3bf6e25d77c71528038a4e Mon Sep 17 00:00:00 2001 From: Filip Sajdak Date: Tue, 29 Nov 2022 23:32:11 +0100 Subject: [PATCH 3/3] Adjust regression-tests to new UFCS --- ...e2-inspect-expression-in-generic-function-multiple-types.cpp | 2 +- .../pure2-inspect-expression-with-as-in-generic-function.cpp | 2 +- .../pure2-inspect-fallback-with-variant-any-optional.cpp | 2 +- ...re2-inspect-generic-void-empty-with-variant-any-optional.cpp | 2 +- regression-tests/test-results/pure2-type-safety-1.cpp | 2 +- .../pure2-type-safety-2-with-inspect-expression.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/regression-tests/test-results/pure2-inspect-expression-in-generic-function-multiple-types.cpp b/regression-tests/test-results/pure2-inspect-expression-in-generic-function-multiple-types.cpp index eec8c3718..c048b6922 100644 --- a/regression-tests/test-results/pure2-inspect-expression-in-generic-function-multiple-types.cpp +++ b/regression-tests/test-results/pure2-inspect-expression-in-generic-function-multiple-types.cpp @@ -32,7 +32,7 @@ auto test_generic(auto const& x) -> void; auto test_generic(auto const& x) -> void{ std::cout - << std::setw(30) << typeid(x).name() + << std::setw(30) << CPP2_UFCS_0(name, typeid(x)) << " value is " << [&] () -> std::string { auto&& __expr = x; if (cpp2::is(__expr)) { if constexpr( requires{"integer " + std::to_string(cpp2::as(x));} ) if constexpr( std::is_convertible_v(x)))),std::string> ) return "integer " + std::to_string(cpp2::as(x)); else return std::string{}; else return std::string{}; } diff --git a/regression-tests/test-results/pure2-inspect-expression-with-as-in-generic-function.cpp b/regression-tests/test-results/pure2-inspect-expression-with-as-in-generic-function.cpp index aef95a5db..783de04c3 100644 --- a/regression-tests/test-results/pure2-inspect-expression-with-as-in-generic-function.cpp +++ b/regression-tests/test-results/pure2-inspect-expression-with-as-in-generic-function.cpp @@ -19,7 +19,7 @@ auto print_an_int(auto const& x) -> void; auto print_an_int(auto const& x) -> void{ std::cout - << std::setw(30) << typeid(x).name() + << std::setw(30) << CPP2_UFCS_0(name, typeid(x)) << " value is " << [&] () -> std::string { auto&& __expr = x; if (cpp2::is(__expr)) { if constexpr( requires{std::to_string(cpp2::as(x));} ) if constexpr( std::is_convertible_v(x)))),std::string> ) return std::to_string(cpp2::as(x)); else return std::string{}; else return std::string{}; } diff --git a/regression-tests/test-results/pure2-inspect-fallback-with-variant-any-optional.cpp b/regression-tests/test-results/pure2-inspect-fallback-with-variant-any-optional.cpp index 889684643..32eea4d3b 100644 --- a/regression-tests/test-results/pure2-inspect-fallback-with-variant-any-optional.cpp +++ b/regression-tests/test-results/pure2-inspect-fallback-with-variant-any-optional.cpp @@ -26,7 +26,7 @@ auto test_generic(auto const& x) -> void; auto test_generic(auto const& x) -> void{ std::cout - << "\n" << typeid(x).name() << "\n ..." + << "\n" << CPP2_UFCS_0(name, typeid(x)) << "\n ..." << [&] () -> std::string { auto&& __expr = x; if (cpp2::is(__expr)) { if constexpr( requires{" matches std::string";} ) if constexpr( std::is_convertible_v ) return " matches std::string"; else return std::string{}; else return std::string{}; } else if (cpp2::is>(__expr)) { if constexpr( requires{" matches std::variant";} ) if constexpr( std::is_convertible_v")),std::string> ) return " matches std::variant"; else return std::string{}; else return std::string{}; } diff --git a/regression-tests/test-results/pure2-inspect-generic-void-empty-with-variant-any-optional.cpp b/regression-tests/test-results/pure2-inspect-generic-void-empty-with-variant-any-optional.cpp index cb99f1ba0..f8e0de52f 100644 --- a/regression-tests/test-results/pure2-inspect-generic-void-empty-with-variant-any-optional.cpp +++ b/regression-tests/test-results/pure2-inspect-generic-void-empty-with-variant-any-optional.cpp @@ -30,7 +30,7 @@ auto test_generic(auto const& x) -> void; auto test_generic(auto const& x) -> void{ std::cout - << "\n" << typeid(x).name() << "\n ..." + << "\n" << CPP2_UFCS_0(name, typeid(x)) << "\n ..." << [&] () -> std::string { auto&& __expr = x; if (cpp2::is(__expr)) { if constexpr( requires{" VOYDE AND EMPTIE";} ) if constexpr( std::is_convertible_v ) return " VOYDE AND EMPTIE"; else return std::string{}; else return std::string{}; } else return " no match"; } diff --git a/regression-tests/test-results/pure2-type-safety-1.cpp b/regression-tests/test-results/pure2-type-safety-1.cpp index 42dd1e90a..0debadab5 100644 --- a/regression-tests/test-results/pure2-type-safety-1.cpp +++ b/regression-tests/test-results/pure2-type-safety-1.cpp @@ -39,7 +39,7 @@ auto print(cpp2::in msg, cpp2::in b) -> void; } auto test_generic(auto const& x) -> void{ - std::string msg { typeid(x).name() }; + std::string msg { CPP2_UFCS_0(name, typeid(x)) }; msg += " is int? "; print( msg, cpp2::is(x)); } diff --git a/regression-tests/test-results/pure2-type-safety-2-with-inspect-expression.cpp b/regression-tests/test-results/pure2-type-safety-2-with-inspect-expression.cpp index 0a265bfef..813a4a37b 100644 --- a/regression-tests/test-results/pure2-type-safety-2-with-inspect-expression.cpp +++ b/regression-tests/test-results/pure2-type-safety-2-with-inspect-expression.cpp @@ -32,7 +32,7 @@ auto test_generic(auto const& x) -> void; auto test_generic(auto const& x) -> void{ std::cout - << std::setw(30) << typeid(x).name() + << std::setw(30) << CPP2_UFCS_0(name, typeid(x)) << " value is " << [&] () -> std::string { auto&& __expr = x; if (cpp2::is(__expr)) { if constexpr( requires{std::to_string(cpp2::as(x));} ) if constexpr( std::is_convertible_v(x)))),std::string> ) return std::to_string(cpp2::as(x)); else return std::string{}; else return std::string{}; }