Skip to content

Commit

Permalink
Allow function parameter default arguments
Browse files Browse the repository at this point in the history
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**)
  • Loading branch information
hsutter committed Aug 9, 2024
1 parent 658d307 commit 873b760
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 16 deletions.
19 changes: 19 additions & 0 deletions regression-tests/pure2-default-arguments.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

// Note: Using source_location requires GCC 11 or higher,
// Clang 16 or higher, MSVC 2019 16.10 or higher.
// Older compilers will emit failures for this test case.
my_function_name: (
fn: *const char = std::source_location::current().function_name()
)
= {
std::cout << "calling: (fn)$\n";
}

f: (x: i32 = 0) = { std::cout << x; }

main: (args) = {
my_function_name();
f();
f(1);
f(2);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pure2-default-arguments.cpp2:6:61: error: no member named 'source_location' in namespace 'std'
char const* fn = CPP2_UFCS_NONLOCAL(function_name)(std::source_location::current())
~~~~~^
1 error generated.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
In file included from pure2-default-arguments.cpp:7:
../../../include/cpp2util.h:2086:28: error: local variable ‘obj’ may not appear in this context
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); }
| ^~~
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); }
| ^~~~~~~~~~~
../../../include/cpp2util.h:2086:15: note: in expansion of macro ‘CPP2_FORWARD’
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); }
| ^~~~~~~~~~~~
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
2107 | { return !x.has_value(); }
| ^~~~~~~~~
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
2137 | { return std::any_cast<T>( x ); }
| ^
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
../../../include/cpp2util.h:2086:92: error: local variable ‘params’ may not appear in this context
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); }
| ^~~~~~
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); }
| ^~~~~~~~~~~
../../../include/cpp2util.h:2086:79: note: in expansion of macro ‘CPP2_FORWARD’
2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); }
| ^~~~~~~~~~~~
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
2107 | { return !x.has_value(); }
| ^~~~~~~~~
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
2137 | { return std::any_cast<T>( x ); }
| ^
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
../../../include/cpp2util.h:2087:74: error: local variable ‘obj’ may not appear in this context
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
| ^~~
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); }
| ^~~~~~~~~~~
../../../include/cpp2util.h:2087:61: note: in expansion of macro ‘CPP2_FORWARD’
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
| ^~~~~~~~~~~~
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
2107 | { return !x.has_value(); }
| ^~~~~~~~~
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
2137 | { return std::any_cast<T>( x ); }
| ^
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
../../../include/cpp2util.h:2087:93: error: local variable ‘params’ may not appear in this context
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
| ^~~~~~
../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’
2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); }
| ^~~~~~~~~~~
../../../include/cpp2util.h:2087:80: note: in expansion of macro ‘CPP2_FORWARD’
2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); }
| ^~~~~~~~~~~~
../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’
2107 | { return !x.has_value(); }
| ^~~~~~~~~
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
2137 | { return std::any_cast<T>( x ); }
| ^
pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
pure2-default-arguments.cpp2:6:61: error: ‘std::source_location’ has not been declared
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
calling: int main(int, char**)
012
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
calling: int __cdecl main(const int,char **)
012
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pure2-default-arguments.cpp
53 changes: 53 additions & 0 deletions regression-tests/test-results/pure2-default-arguments.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

#define CPP2_IMPORT_STD Yes

//=== Cpp2 type declarations ====================================================


#include "cpp2util.h"

#line 1 "pure2-default-arguments.cpp2"


//=== Cpp2 type definitions and function declarations ===========================

#line 1 "pure2-default-arguments.cpp2"

// Note: Using source_location requires GCC 11 or higher,
// Clang 16 or higher, MSVC 2019 16.10 or higher.
// Older compilers will emit failures for this test case.
#line 5 "pure2-default-arguments.cpp2"
auto my_function_name(
char const* fn = CPP2_UFCS_NONLOCAL(function_name)(std::source_location::current())
) -> void;

#line 12 "pure2-default-arguments.cpp2"
auto f(cpp2::impl::in<cpp2::i32> x = 0) -> void;

auto main(int const argc_, char** argv_) -> int;

//=== Cpp2 function definitions =================================================

#line 1 "pure2-default-arguments.cpp2"

#line 5 "pure2-default-arguments.cpp2"
auto my_function_name(
char const* fn
) -> void
{
std::cout << "calling: " + cpp2::to_string(fn) + "\n";
}

#line 12 "pure2-default-arguments.cpp2"
auto f(cpp2::impl::in<cpp2::i32> x) -> void{std::cout << x; }

#line 14 "pure2-default-arguments.cpp2"
auto main(int const argc_, char** argv_) -> int{
auto const args = cpp2::make_args(argc_, argv_);
#line 15 "pure2-default-arguments.cpp2"
my_function_name();
f();
f(1);
f(2);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pure2-default-arguments.cpp2... ok (all Cpp2, passes safety checks)

2 changes: 1 addition & 1 deletion regression-tests/test-results/version
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

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

SPDX-License-Identifier: CC-BY-NC-ND-4.0
Expand Down
2 changes: 1 addition & 1 deletion source/build.info
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"9804:1033"
"9809:1046"
21 changes: 10 additions & 11 deletions source/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -2857,8 +2857,9 @@ struct declaration_node
bool member_function_generation = true;

// Cache some context
bool is_a_template_parameter = false;
bool is_a_parameter = false;
bool is_a_template_parameter = false;
bool is_a_parameter = false;
bool is_a_statement_parameter = false;

// Constructor
//
Expand All @@ -2885,6 +2886,12 @@ struct declaration_node
return is_a_parameter;
}

auto is_statement_parameter() const
-> bool
{
return is_a_statement_parameter;
}

auto type_member_mark_for_removal()
-> bool
{
Expand Down Expand Up @@ -8041,6 +8048,7 @@ class parser
pos = start_pos; // backtrack
return {};
}
n->declaration->is_a_statement_parameter = is_statement;

// And some error checks
//
Expand Down Expand Up @@ -8107,15 +8115,6 @@ class parser
return {};
}

if (
!is_returns
&& !is_statement
&& n->declaration->initializer
)
{
error("Cpp2 is currently exploring the path of not allowing default arguments - use overloading instead", false);
return {};
}
if (is_named && is_returns) {
auto tok = n->name();
assert(tok);
Expand Down
14 changes: 11 additions & 3 deletions source/to_cpp1.h
Original file line number Diff line number Diff line change
Expand Up @@ -3291,14 +3291,18 @@ class cppfront
ufcs_string += "_TEMPLATE";
}

// If we're in an object declaration (i.e., initializer)
// at namespace scope, use the _NONLOCAL version
// If we're in a namespace-scope object declaration (i.e., initializer)
// or in a default function argument, use the _NONLOCAL version
//
// Note: If there are other cases where code could execute
// in a non-local scope where a capture-default for the UFCS
// lambda would not be allowed, then add them here
if (
current_declarations.back()->is_namespace()
|| (
current_declarations.back()->is_parameter()
&& !current_declarations.back()->is_statement_parameter()
)
|| (
current_declarations.back()->is_object()
&& current_declarations.back()->parent_is_namespace()
Expand Down Expand Up @@ -4680,6 +4684,10 @@ class cppfront
if (
!is_returns
&& n.declaration->initializer
&& (
is_statement
|| printer.get_phase() != printer.phase2_func_defs
)
)
{
auto guard = stack_element(current_declarations, &*n.declaration);
Expand All @@ -4689,7 +4697,7 @@ class cppfront
else {
printer.print_cpp2( " = ", n.declaration->initializer->position() );
}
emit(*n.declaration->initializer, !is_statement);
emit(*n.declaration->initializer, false);
if (is_statement) {
printer.print_cpp2( "};", n.declaration->initializer->position() );
}
Expand Down

0 comments on commit 873b760

Please sign in to comment.