Skip to content

Commit 873b760

Browse files
committed
Allow function parameter default arguments
Closes #1189 I had been experimenting with not allowing default arguments for function parameters, in part because of the potential for creating order-dependent code; the way to find out whether they're necessary is to not support them and see if that leaves a usability hole. The result of the experiment is that it does leave a hole: There's persistent feedback that default arguments are often useful, and are actually necessary for a few cases including particularly `std::source_location` parameters. As for order independence, there are already ways to opt into creating potentially order-dependent code (such as by deduced return types which depend on function bodies). So I think it's time to enable default arguments, and Cpp2 is still order-independent by default. This example now works: my_function_name: ( fn: *const char = std::source_location::current().function_name() ) = { std::cout << "calling: (fn)$\n"; } main: (args) = { my_function_name(); } // On MSVC 2022, prints: // calling: int __cdecl main(const int,char **) // On GCC 14, prints: // calling: int main(int, char**)
1 parent 658d307 commit 873b760

12 files changed

+172
-16
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
// Note: Using source_location requires GCC 11 or higher,
3+
// Clang 16 or higher, MSVC 2019 16.10 or higher.
4+
// Older compilers will emit failures for this test case.
5+
my_function_name: (
6+
fn: *const char = std::source_location::current().function_name()
7+
)
8+
= {
9+
std::cout << "calling: (fn)$\n";
10+
}
11+
12+
f: (x: i32 = 0) = { std::cout << x; }
13+
14+
main: (args) = {
15+
my_function_name();
16+
f();
17+
f(1);
18+
f(2);
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pure2-default-arguments.cpp2:6:61: error: no member named 'source_location' in namespace 'std'
2+
char const* fn = CPP2_UFCS_NONLOCAL(function_name)(std::source_location::current())
3+
~~~~~^
4+
1 error generated.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
In file included from pure2-default-arguments.cpp:7:
2+
../../../include/cpp2util.h:2086:28: error: local variable ‘obj’ may not appear in this context
3+
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); }
4+
| ^~~
5+
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
6+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); }
7+
| ^~~~~~~~~~~
8+
../../../include/cpp2util.h:2086:15: note: in expansion of macro ‘CPP2_FORWARD’
9+
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); }
10+
| ^~~~~~~~~~~~
11+
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
12+
2107 | { return !x.has_value(); }
13+
| ^~~~~~~~~
14+
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
15+
2137 | { return std::any_cast<T>( x ); }
16+
| ^
17+
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
18+
../../../include/cpp2util.h:2086:92: error: local variable ‘params’ may not appear in this context
19+
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); }
20+
| ^~~~~~
21+
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
22+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); }
23+
| ^~~~~~~~~~~
24+
../../../include/cpp2util.h:2086:79: note: in expansion of macro ‘CPP2_FORWARD’
25+
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); }
26+
| ^~~~~~~~~~~~
27+
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
28+
2107 | { return !x.has_value(); }
29+
| ^~~~~~~~~
30+
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
31+
2137 | { return std::any_cast<T>( x ); }
32+
| ^
33+
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
34+
../../../include/cpp2util.h:2087:74: error: local variable ‘obj’ may not appear in this context
35+
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
36+
| ^~~
37+
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
38+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); }
39+
| ^~~~~~~~~~~
40+
../../../include/cpp2util.h:2087:61: note: in expansion of macro ‘CPP2_FORWARD’
41+
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
42+
| ^~~~~~~~~~~~
43+
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
44+
2107 | { return !x.has_value(); }
45+
| ^~~~~~~~~
46+
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
47+
2137 | { return std::any_cast<T>( x ); }
48+
| ^
49+
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
50+
../../../include/cpp2util.h:2087:93: error: local variable ‘params’ may not appear in this context
51+
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
52+
| ^~~~~~
53+
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
54+
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); }
55+
| ^~~~~~~~~~~
56+
../../../include/cpp2util.h:2087:80: note: in expansion of macro ‘CPP2_FORWARD’
57+
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
58+
| ^~~~~~~~~~~~
59+
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
60+
2107 | { return !x.has_value(); }
61+
| ^~~~~~~~~
62+
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
63+
2137 | { return std::any_cast<T>( x ); }
64+
| ^
65+
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
66+
pure2-default-arguments.cpp2:6:61: error: ‘std::source_location’ has not been declared
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
calling: int main(int, char**)
2+
012
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
calling: int __cdecl main(const int,char **)
2+
012
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pure2-default-arguments.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
#define CPP2_IMPORT_STD Yes
3+
4+
//=== Cpp2 type declarations ====================================================
5+
6+
7+
#include "cpp2util.h"
8+
9+
#line 1 "pure2-default-arguments.cpp2"
10+
11+
12+
//=== Cpp2 type definitions and function declarations ===========================
13+
14+
#line 1 "pure2-default-arguments.cpp2"
15+
16+
// Note: Using source_location requires GCC 11 or higher,
17+
// Clang 16 or higher, MSVC 2019 16.10 or higher.
18+
// Older compilers will emit failures for this test case.
19+
#line 5 "pure2-default-arguments.cpp2"
20+
auto my_function_name(
21+
char const* fn = CPP2_UFCS_NONLOCAL(function_name)(std::source_location::current())
22+
) -> void;
23+
24+
#line 12 "pure2-default-arguments.cpp2"
25+
auto f(cpp2::impl::in<cpp2::i32> x = 0) -> void;
26+
27+
auto main(int const argc_, char** argv_) -> int;
28+
29+
//=== Cpp2 function definitions =================================================
30+
31+
#line 1 "pure2-default-arguments.cpp2"
32+
33+
#line 5 "pure2-default-arguments.cpp2"
34+
auto my_function_name(
35+
char const* fn
36+
) -> void
37+
{
38+
std::cout << "calling: " + cpp2::to_string(fn) + "\n";
39+
}
40+
41+
#line 12 "pure2-default-arguments.cpp2"
42+
auto f(cpp2::impl::in<cpp2::i32> x) -> void{std::cout << x; }
43+
44+
#line 14 "pure2-default-arguments.cpp2"
45+
auto main(int const argc_, char** argv_) -> int{
46+
auto const args = cpp2::make_args(argc_, argv_);
47+
#line 15 "pure2-default-arguments.cpp2"
48+
my_function_name();
49+
f();
50+
f(1);
51+
f(2);
52+
}
53+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pure2-default-arguments.cpp2... ok (all Cpp2, passes safety checks)
2+

regression-tests/test-results/version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
cppfront compiler v0.7.2 Build 9804:1033
2+
cppfront compiler v0.7.2 Build 9809:1046
33
Copyright(c) Herb Sutter All rights reserved
44

55
SPDX-License-Identifier: CC-BY-NC-ND-4.0

source/build.info

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"9804:1033"
1+
"9809:1046"

source/parse.h

+10-11
Original file line numberDiff line numberDiff line change
@@ -2857,8 +2857,9 @@ struct declaration_node
28572857
bool member_function_generation = true;
28582858

28592859
// Cache some context
2860-
bool is_a_template_parameter = false;
2861-
bool is_a_parameter = false;
2860+
bool is_a_template_parameter = false;
2861+
bool is_a_parameter = false;
2862+
bool is_a_statement_parameter = false;
28622863

28632864
// Constructor
28642865
//
@@ -2885,6 +2886,12 @@ struct declaration_node
28852886
return is_a_parameter;
28862887
}
28872888

2889+
auto is_statement_parameter() const
2890+
-> bool
2891+
{
2892+
return is_a_statement_parameter;
2893+
}
2894+
28882895
auto type_member_mark_for_removal()
28892896
-> bool
28902897
{
@@ -8041,6 +8048,7 @@ class parser
80418048
pos = start_pos; // backtrack
80428049
return {};
80438050
}
8051+
n->declaration->is_a_statement_parameter = is_statement;
80448052

80458053
// And some error checks
80468054
//
@@ -8107,15 +8115,6 @@ class parser
81078115
return {};
81088116
}
81098117

8110-
if (
8111-
!is_returns
8112-
&& !is_statement
8113-
&& n->declaration->initializer
8114-
)
8115-
{
8116-
error("Cpp2 is currently exploring the path of not allowing default arguments - use overloading instead", false);
8117-
return {};
8118-
}
81198118
if (is_named && is_returns) {
81208119
auto tok = n->name();
81218120
assert(tok);

source/to_cpp1.h

+11-3
Original file line numberDiff line numberDiff line change
@@ -3291,14 +3291,18 @@ class cppfront
32913291
ufcs_string += "_TEMPLATE";
32923292
}
32933293

3294-
// If we're in an object declaration (i.e., initializer)
3295-
// at namespace scope, use the _NONLOCAL version
3294+
// If we're in a namespace-scope object declaration (i.e., initializer)
3295+
// or in a default function argument, use the _NONLOCAL version
32963296
//
32973297
// Note: If there are other cases where code could execute
32983298
// in a non-local scope where a capture-default for the UFCS
32993299
// lambda would not be allowed, then add them here
33003300
if (
33013301
current_declarations.back()->is_namespace()
3302+
|| (
3303+
current_declarations.back()->is_parameter()
3304+
&& !current_declarations.back()->is_statement_parameter()
3305+
)
33023306
|| (
33033307
current_declarations.back()->is_object()
33043308
&& current_declarations.back()->parent_is_namespace()
@@ -4680,6 +4684,10 @@ class cppfront
46804684
if (
46814685
!is_returns
46824686
&& n.declaration->initializer
4687+
&& (
4688+
is_statement
4689+
|| printer.get_phase() != printer.phase2_func_defs
4690+
)
46834691
)
46844692
{
46854693
auto guard = stack_element(current_declarations, &*n.declaration);
@@ -4689,7 +4697,7 @@ class cppfront
46894697
else {
46904698
printer.print_cpp2( " = ", n.declaration->initializer->position() );
46914699
}
4692-
emit(*n.declaration->initializer, !is_statement);
4700+
emit(*n.declaration->initializer, false);
46934701
if (is_statement) {
46944702
printer.print_cpp2( "};", n.declaration->initializer->position() );
46954703
}

0 commit comments

Comments
 (0)