Skip to content

Commit 9e568b7

Browse files
committed
Disable UFCS for multiple template args, closes #82
Alpha limitation: Macros don't like commas Happy to entertain a better solution that makes it work, feel free to submit an issue or better yet a PR if you have a good solution that passes regression tests but also makes `x.f<a,b>()` successfully call a non-member `f`.
1 parent c7bb436 commit 9e568b7

File tree

5 files changed

+92
-2
lines changed

5 files changed

+92
-2
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
template <auto from, auto to>
3+
auto substr(const std::string& input) -> std::string {
4+
return input.substr(from, to-from);
5+
}
6+
7+
struct X {
8+
std::string input;
9+
X(const std::string& input) : input{input} { }
10+
template <auto from, auto to>
11+
auto substr() -> std::string {
12+
return input.substr(from, to-from);
13+
}
14+
};
15+
16+
main: () -> int = {
17+
test_string: std::string = "The rain in Spain flows mainly down the drain";
18+
std::cout << substr<4,8>(test_string) << "\n";
19+
20+
x: X = test_string;
21+
std::cout << x.substr<4,8>() << "\n";
22+
// for now this should not be UFCS-ized because of the multiple template arguments
23+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// ----- Cpp2 support -----
2+
#include "cpp2util.h"
3+
4+
#line 1 "mixed-ufcs-multiple-template-arguments.cpp2"
5+
6+
template <auto from, auto to>
7+
auto substr(const std::string& input) -> std::string {
8+
return input.substr(from, to-from);
9+
}
10+
11+
struct X {
12+
std::string input;
13+
X(const std::string& input) : input{input} { }
14+
template <auto from, auto to>
15+
auto substr() -> std::string {
16+
return input.substr(from, to-from);
17+
}
18+
};
19+
20+
[[nodiscard]] auto main() -> int;
21+
22+
//=== Cpp2 definitions ==========================================================
23+
24+
#line 15 "mixed-ufcs-multiple-template-arguments.cpp2"
25+
26+
[[nodiscard]] auto main() -> int{
27+
std::string test_string { "The rain in Spain flows mainly down the drain" };
28+
std::cout << substr<4,8>(test_string) << "\n";
29+
30+
X x { test_string };
31+
std::cout << std::move(x).substr<4,8>() << "\n";
32+
// for now this should not be UFCS-ized because of the multiple template arguments
33+
}
34+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mixed-ufcs-multiple-template-arguments.cpp2... ok (mixed Cpp1/Cpp2, Cpp2 code passes safety checks)
2+

source/cppfront.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,6 +1684,9 @@ class cppfront
16841684
std::ssize(n.ops) >= 2 && // and we're of the form:
16851685
n.ops[0].op->type() == lexeme::Dot && // token . id-expr ( expr-list )
16861686
n.ops[1].op->type() == lexeme::LeftParen &&
1687+
// alpha limitation: if it's a function call with more than one template argument (e.g., x.f<1,2>())
1688+
// the UFCS* macros can't handle that right now, so don't UFCS-size it
1689+
n.ops[0].id_expr->template_args_count() < 2 &&
16871690
// and either there's nothing after that, or there's just a $ after that
16881691
(
16891692
std::ssize(n.ops) == 2 ||
@@ -1715,8 +1718,7 @@ class cppfront
17151718
//printer.print_cpp2("CPP2_UFCS(", n.position());
17161719

17171720
auto ufcs_string = std::string("CPP2_UFCS");
1718-
// If we can get the id-expr's token, then the unqualified-id has no template args
1719-
if (n.ops[0].id_expr->get_token() == nullptr) {
1721+
if (n.ops[0].id_expr->template_args_count() > 0) {
17201722
ufcs_string += "_TEMPLATE";
17211723
}
17221724
// If there are no additional arguments, use the _0 version

source/parse.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ struct primary_expression_node
130130
std::unique_ptr<inspect_expression_node>
131131
> expr;
132132

133+
auto template_args_count() -> int;
133134
auto get_token() -> token const*;
134135
auto position() const -> source_position;
135136
auto visit(auto& v, int depth) -> void;
@@ -385,6 +386,10 @@ struct unqualified_id_node
385386
};
386387
std::vector<term> template_args;
387388

389+
auto template_args_count() -> int {
390+
return std::ssize(template_args);
391+
}
392+
388393
auto get_token() -> token const* {
389394
if (template_args.empty()) {
390395
assert (identifier);
@@ -469,6 +474,14 @@ struct type_id_node
469474
std::unique_ptr<unqualified_id_node>
470475
> id;
471476

477+
auto template_args_count() -> int {
478+
if (id.index() == unqualified) {
479+
return std::get<unqualified>(id)->template_args_count();
480+
}
481+
// else
482+
return 0;
483+
}
484+
472485
auto get_token() -> token const* {
473486
if (id.index() == unqualified) {
474487
return std::get<unqualified>(id)->get_token();
@@ -505,6 +518,14 @@ struct id_expression_node
505518
std::unique_ptr<unqualified_id_node>
506519
> id;
507520

521+
auto template_args_count() -> int {
522+
if (id.index() == unqualified) {
523+
return std::get<unqualified>(id)->template_args_count();
524+
}
525+
// else
526+
return 0;
527+
}
528+
508529
auto get_token() -> token const* {
509530
if (id.index() == unqualified) {
510531
return std::get<unqualified>(id)->get_token();
@@ -933,6 +954,14 @@ struct declaration_node
933954
}
934955
};
935956

957+
auto primary_expression_node::template_args_count() -> int {
958+
if (expr.index() == id_expression) {
959+
return std::get<id_expression>(expr)->template_args_count();
960+
}
961+
// else
962+
return 0;
963+
}
964+
936965
auto primary_expression_node::get_token() -> token const*
937966
{
938967
if (expr.index() == identifier) {

0 commit comments

Comments
 (0)