Skip to content

Commit 0f45579

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 d7a7332 commit 0f45579

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
@@ -1667,17 +1667,19 @@ class cppfront
16671667
captured_part += "_" + std::to_string(mynum);
16681668
}
16691669

1670-
// Check to see if it's just a function call with "." syntax,
1670+
// Check to see if it's just a function call with "." syntax (potentially chained with other methods),
16711671
// and if so use this path to convert it to UFCS
16721672
if (// there's a single-token expression followed by . and (
16731673
n.expr->get_token() && // if the base expression is a single token
16741674
std::ssize(n.ops) >= 2 && // and we're of the form:
16751675
n.ops[0].op->type() == lexeme::Dot && // token . id-expr ( expr-list )
16761676
n.ops[1].op->type() == lexeme::LeftParen &&
16771677
// and either there's nothing after that, or there's just a $ after that
1678+
// or there is mathod chaining started
16781679
(
16791680
std::ssize(n.ops) == 2 ||
1680-
(std::ssize(n.ops) == 3 && n.ops[2].op->type() == lexeme::Dollar)
1681+
(std::ssize(n.ops) == 3 && n.ops[2].op->type() == lexeme::Dollar) ||
1682+
(std::ssize(n.ops) > 3 && n.ops[2].op->type() == lexeme::Dot) // fsajdak: chaining identification
16811683
)
16821684
)
16831685
{
@@ -1690,44 +1692,52 @@ class cppfront
16901692

16911693
// Otherwise, do the UFCS work...
16921694

1693-
// The . has its id_expr
1694-
assert (n.ops[0].id_expr);
1695-
1696-
// The ( has its expr_list and op_close
1697-
assert (n.ops[1].expr_list && n.ops[1].op_close);
1698-
1699-
//--------------------------------------------------------------------
1700-
// TODO: When MSVC supports __VA_OPT__ in standard mode without the
1701-
// experimental /Zc:preprocessor switch, use this single line
1702-
// instead of the dual lines below that special-case _0 args
1703-
// AND: Make the similarly noted change in cpp2util.h
1704-
//
1705-
//printer.print_cpp2("CPP2_UFCS(", n.position());
1706-
1707-
// If there are no additional arguments, use the CPP2_UFCS_0 version
1708-
if (!n.ops[1].expr_list->expressions.empty()) {
1709-
printer.print_cpp2("CPP2_UFCS(", n.position());
1710-
}
1711-
else {
1712-
printer.print_cpp2("CPP2_UFCS_0(", n.position());
1695+
// If method are chained we need to go from the last to the first
1696+
// token.a(a-expr-list).b(b-expr-list).c(c-expr-list) will be tranformed to:
1697+
// CPP2_UFCS(c, CPP2_UFCS(b, CPP2_UFCS(a,token, a-expr-list), b-expr-list), c-expr-list )
1698+
for (auto i = std::ssize(n.ops)-1; i > 0; i -= 2)
1699+
{
1700+
// The . has its id_expr
1701+
assert (n.ops[i-1].id_expr);
1702+
1703+
// The ( has its expr_list and op_close
1704+
assert (n.ops[i].expr_list && n.ops[i].op_close);
1705+
1706+
//--------------------------------------------------------------------
1707+
// TODO: When MSVC supports __VA_OPT__ in standard mode without the
1708+
// experimental /Zc:preprocessor switch, use this single line
1709+
// instead of the dual lines below that special-case _0 args
1710+
// AND: Make the similarly noted change in cpp2util.h
1711+
//
1712+
//printer.print_cpp2("CPP2_UFCS(", n.position());
1713+
1714+
// If there are no additional arguments, use the CPP2_UFCS_0 version
1715+
if (!n.ops[i].expr_list->expressions.empty()) {
1716+
printer.print_cpp2("CPP2_UFCS(", n.position());
1717+
}
1718+
else {
1719+
printer.print_cpp2("CPP2_UFCS_0(", n.position());
1720+
}
1721+
emit(*n.ops[i-1].id_expr);
1722+
printer.print_cpp2(", ", n.position());
17131723
}
1714-
//--------------------------------------------------------------------
17151724

1716-
// Make the "funcname" the first argument to CPP2_UFCS
1717-
emit(*n.ops[0].id_expr);
1718-
printer.print_cpp2(", ", n.position());
1719-
1720-
// Then make the base expression the second argument
1721-
emit(*n.expr);
1725+
// expr-list need to be added in reversed order then CPP2_UFCS macros
1726+
for (auto i = 0; i < std::ssize(n.ops); i += 2) {
1727+
// Then make the base expression the second argument - only needed on the most nested call
1728+
if (i == 0) {
1729+
emit(*n.expr);
1730+
}
17221731

1723-
// Then tack on any additional arguments
1724-
if (!n.ops[1].expr_list->expressions.empty()) {
1725-
printer.print_cpp2(", ", n.position());
1726-
push_need_expression_list_parens(false);
1727-
emit(*n.ops[1].expr_list);
1728-
pop_need_expression_list_parens();
1732+
// Then tack on any additional arguments
1733+
if (!n.ops[(i+1)].expr_list->expressions.empty()) {
1734+
printer.print_cpp2(", ", n.position());
1735+
push_need_expression_list_parens(false);
1736+
emit(*n.ops[(i+1)].expr_list);
1737+
pop_need_expression_list_parens();
1738+
}
1739+
printer.print_cpp2(")", n.position());
17291740
}
1730-
printer.print_cpp2(")", n.position());
17311741

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

0 commit comments

Comments
 (0)