Skip to content

Commit 00f2574

Browse files
committed
UFCS: add chaining to the UFCS calls
Former implementation was not supporting method chaining ```cpp v.a().b().c(); // failed to use UFCS ``` The change converts above code into nested call of `CPP2_UFCS` macros: ```cpp CPP2_UFCS_0(c, CPP2_UFCS_0(b, CPP2_UFCS_0(a, v) ) ); ``` Functions with expr-list are also handled.
1 parent bd218d0 commit 00f2574

File tree

1 file changed

+46
-36
lines changed

1 file changed

+46
-36
lines changed

source/cppfront.cpp

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,17 +1634,19 @@ class cppfront
16341634
captured_part += "_" + std::to_string(mynum);
16351635
}
16361636

1637-
// Check to see if it's just a function call with "." syntax,
1637+
// Check to see if it's just a function call with "." syntax (potentially chained with other methods),
16381638
// and if so use this path to convert it to UFCS
16391639
if (// there's a single-token expression followed by . and (
16401640
n.expr->get_token() && // if the base expression is a single token
16411641
std::ssize(n.ops) >= 2 && // and we're of the form:
16421642
n.ops[0].op->type() == lexeme::Dot && // token . id-expr ( expr-list )
16431643
n.ops[1].op->type() == lexeme::LeftParen &&
16441644
// and either there's nothing after that, or there's just a $ after that
1645+
// or there is mathod chaining started
16451646
(
16461647
std::ssize(n.ops) == 2 ||
1647-
(std::ssize(n.ops) == 3 && n.ops[2].op->type() == lexeme::Dollar)
1648+
(std::ssize(n.ops) == 3 && n.ops[2].op->type() == lexeme::Dollar) ||
1649+
(std::ssize(n.ops) > 3 && n.ops[2].op->type() == lexeme::Dot) // fsajdak: chaining identification
16481650
)
16491651
)
16501652
{
@@ -1657,44 +1659,52 @@ class cppfront
16571659

16581660
// Otherwise, do the UFCS work...
16591661

1660-
// The . has its id_expr
1661-
assert (n.ops[0].id_expr);
1662-
1663-
// The ( has its expr_list and op_close
1664-
assert (n.ops[1].expr_list && n.ops[1].op_close);
1665-
1666-
//--------------------------------------------------------------------
1667-
// TODO: When MSVC supports __VA_OPT__ in standard mode without the
1668-
// experimental /Zc:preprocessor switch, use this single line
1669-
// instead of the dual lines below that special-case _0 args
1670-
// AND: Make the similarly noted change in cpp2util.h
1671-
//
1672-
//printer.print_cpp2("CPP2_UFCS(", n.position());
1673-
1674-
// If there are no additional arguments, use the CPP2_UFCS_0 version
1675-
if (!n.ops[1].expr_list->expressions.empty()) {
1676-
printer.print_cpp2("CPP2_UFCS(", n.position());
1677-
}
1678-
else {
1679-
printer.print_cpp2("CPP2_UFCS_0(", n.position());
1662+
// If method are chained we need to go from the last to the first
1663+
// token.a(a-expr-list).b(b-expr-list).c(c-expr-list) will be tranformed to:
1664+
// CPP2_UFCS(c, CPP2_UFCS(b, CPP2_UFCS(a,token, a-expr-list), b-expr-list), c-expr-list )
1665+
for (auto i = std::ssize(n.ops)-1; i > 0; i -= 2)
1666+
{
1667+
// The . has its id_expr
1668+
assert (n.ops[i-1].id_expr);
1669+
1670+
// The ( has its expr_list and op_close
1671+
assert (n.ops[i].expr_list && n.ops[i].op_close);
1672+
1673+
//--------------------------------------------------------------------
1674+
// TODO: When MSVC supports __VA_OPT__ in standard mode without the
1675+
// experimental /Zc:preprocessor switch, use this single line
1676+
// instead of the dual lines below that special-case _0 args
1677+
// AND: Make the similarly noted change in cpp2util.h
1678+
//
1679+
//printer.print_cpp2("CPP2_UFCS(", n.position());
1680+
1681+
// If there are no additional arguments, use the CPP2_UFCS_0 version
1682+
if (!n.ops[i].expr_list->expressions.empty()) {
1683+
printer.print_cpp2("CPP2_UFCS(", n.position());
1684+
}
1685+
else {
1686+
printer.print_cpp2("CPP2_UFCS_0(", n.position());
1687+
}
1688+
emit(*n.ops[i-1].id_expr);
1689+
printer.print_cpp2(", ", n.position());
16801690
}
1681-
//--------------------------------------------------------------------
16821691

1683-
// Make the "funcname" the first argument to CPP2_UFCS
1684-
emit(*n.ops[0].id_expr);
1685-
printer.print_cpp2(", ", n.position());
1686-
1687-
// Then make the base expression the second argument
1688-
emit(*n.expr);
1692+
// expr-list need to be added in reversed order then CPP2_UFCS macros
1693+
for (auto i = 0; i < std::ssize(n.ops); i += 2) {
1694+
// Then make the base expression the second argument - only needed on the most nested call
1695+
if (i == 0) {
1696+
emit(*n.expr);
1697+
}
16891698

1690-
// Then tack on any additional arguments
1691-
if (!n.ops[1].expr_list->expressions.empty()) {
1692-
printer.print_cpp2(", ", n.position());
1693-
push_need_expression_list_parens(false);
1694-
emit(*n.ops[1].expr_list);
1695-
pop_need_expression_list_parens();
1699+
// Then tack on any additional arguments
1700+
if (!n.ops[(i+1)].expr_list->expressions.empty()) {
1701+
printer.print_cpp2(", ", n.position());
1702+
push_need_expression_list_parens(false);
1703+
emit(*n.ops[(i+1)].expr_list);
1704+
pop_need_expression_list_parens();
1705+
}
1706+
printer.print_cpp2(")", n.position());
16961707
}
1697-
printer.print_cpp2(")", n.position());
16981708

16991709
// And we're done. This path has handled this node, so return...
17001710
return;

0 commit comments

Comments
 (0)