diff --git a/docs/cpp2/contracts.md b/docs/cpp2/contracts.md
index a05bf948dc..fcdc21a0db 100644
--- a/docs/cpp2/contracts.md
+++ b/docs/cpp2/contracts.md
@@ -57,7 +57,7 @@ Contract groups are useful to enable or disable or [set custom handlers](#violat
You can create new contract groups just by creating new objects that have a `.report_violation` function. The object's name is the contract group's name. The object can be at any scope: local, global, or heap.
-For example, here are some ways to use contract groups of type [`cpp2::contract_group`](#violation_handlers), which is a convenient group type:
+For example, here are some ways to use contract groups of type [`cpp2::contract_group`](#violation-handlers), which is a convenient group type:
``` cpp title="Using contract groups" hl_lines="1 4 6 10-12"
group_a: cpp2::contract_group = (); // a global group
diff --git a/docs/cpp2/functions.md b/docs/cpp2/functions.md
index 5bfcd0efcf..4771f7b13f 100644
--- a/docs/cpp2/functions.md
+++ b/docs/cpp2/functions.md
@@ -12,7 +12,9 @@ func: ( /* no parameters */ ) = { /* empty body */ }
```
-## Parameters
+## Function signatures: Parameters, returns, and using function types
+
+### Parameters
The parameter list is a [list](common.md#lists) enclosed by `(` `)` parentheses. Each parameter is declared using the [same unified syntax](declarations.md) as used for all declarations. For example:
@@ -72,7 +74,7 @@ wrap_f: (
```
-## Return values
+### Return values
A function can return either of the following. The default is `#!cpp -> void`.
@@ -152,7 +154,7 @@ main: () = {
```
-### Function outputs are not implicitly discardable
+#### Function outputs are not implicitly discardable
A function's outputs are its return values, and the "out" state of any `out` and `inout` parameters.
@@ -200,9 +202,29 @@ main: ()
> - A function call written in Cpp2 `x.f()` member call syntax always treats a non-`#!cpp void` return type as not discardable, even if the function was written in Cpp1 syntax that did not write `[[nodiscard]]`.
+### Using function types
+
+The same function parameter/return syntax can be used as a function type, for example to instantiate `std::function` or to declare a pointer to function variable. For example:
+
+``` cpp title="Using function types with std::function and *pfunc" hl_lines="4 7"
+decorate_int: (i: i32) -> std::string = "--> (i)$ <--";
+
+main: () = {
+ pf1: std::function< (i: i32) -> std::string > = decorate_int&;
+ std::cout << "pf1(123) returned \"(pf1(123))$\"\n";
+
+ pf2: * (i: i32) -> std::string = decorate_int&;
+ std::cout << "pf2(456) returned \"(pf2(456))$\"\n";
+}
+// Prints:
+// pf1 returned "--> 123 <--"
+// pf2 returned "--> 456 <--"
+```
+
+
## Control flow
-## `#!cpp if`, `#!cpp else` — Branches
+### `#!cpp if`, `#!cpp else` — Branches
`if` and `else` are like always in C++, except that `(` `)` parentheses around the condition are not required. Instead, `{` `}` braces around a branch body *are* required. For example:
@@ -216,7 +238,7 @@ else {
```
-## `#!cpp for`, `#!cpp while`, `#!cpp do` — Loops
+### `#!cpp for`, `#!cpp while`, `#!cpp do` — Loops
**`#!cpp do`** and **`#!cpp while`** are like always in C++, except that `(` `)` parentheses around the condition are not required. Instead, `{` `}` braces around the loop body *are* required.
@@ -296,7 +318,7 @@ Line by line:
- `next i++`: The end-of-loop-iteration statement. Note `++` is always postfix in Cpp2.
-### Loop names, `#!cpp break`, and `#!cpp continue`
+#### Loop names, `#!cpp break`, and `#!cpp continue`
Loops can be named using the usual **name `:`** syntax that introduces all names, and `#!cpp break` and `#!cpp continue` can refer to those names. For example:
diff --git a/include/cpp2util.h b/include/cpp2util.h
index b724d0d914..62665049d8 100644
--- a/include/cpp2util.h
+++ b/include/cpp2util.h
@@ -518,15 +518,17 @@ inline std::string join(List const& list) {
//
// Conveniences for expressing Cpp1 references (rarely useful)
//
-// Note: Only needed in rare cases to take full control of matching a
-// Cpp1 signature exactly. Most cases don't need this, for example
-// a Cpp1 virtual function signature declaration like
+// Note: Only needed in rare cases to take full control of matching an
+// odd Cpp1 signature exactly. Most cases don't need this... for
+// example, a Cpp1 virtual function signature declaration like
//
-// virtual void f(int&) const
+// virtual void myfunc(int& val) const
//
// can already be directly overriden by a Cpp2 declaration of
//
-// f: (override this, inout val : int)
+// myfunc: (override this, inout val: int)
+// // identical to this in Cpp1 syntax:
+// // void myfunc(int& val) const override
//
// without any need to say cpp1_ref on the int parameter.
//
diff --git a/regression-tests/pure2-function-typeids.cpp2 b/regression-tests/pure2-function-typeids.cpp2
new file mode 100644
index 0000000000..5e3112522a
--- /dev/null
+++ b/regression-tests/pure2-function-typeids.cpp2
@@ -0,0 +1,102 @@
+
+// --- Scaffolding
+
+f: () = std::cout << "hello world!\n";
+
+g_in : ( s: std::string) = std::cout << "Come in, (s)$\n";
+g_inout: (inout s: std::string) = std::cout << "Come in awhile, but take some biscuits on your way out, (s)$!\n";
+g_out : (out s: std::string) = s = "A Powerful Mage";
+g_move : (move s: std::string) = std::cout << "I hear you've moving, (s)$?\n";
+
+h_forward: (inout s: std::string) -> forward std::string = { std::cout << "Inout (s)$ ... "; return s; }
+h_out : ( s: std::string) -> std::string = { std::cout << "In (s)$ ... "; return "yohoho"; }
+
+f1: (a: std::function< (x:int) -> int >) -> int = a(1);
+f2: (a: * (x:int) -> int ) -> int = a(2);
+g : (x:int) -> int = x+42;
+
+
+// --- Tests for type aliases
+
+A_h_forward: type == (inout s: std::string) -> forward std::string;
+
+
+main: () =
+{
+ // --- Test basic/degenerate cases
+
+ // Test std::function< void() >
+ ff: std::function< () -> void > = f&;
+ ff();
+
+ // Ordinary pointer to function, deduced (always worked)
+ pf: * () -> void = f&;
+ pf();
+
+
+ // --- Tests for parameters
+ // Note: Not forward parameters which imply a template...
+ // function type-ids are for single function signatures
+
+ fg_in : std::function< ( s: std::string) -> void > = g_in&;
+ fg_inout: std::function< (inout s: std::string) -> void > = g_inout&;
+ fg_out : std::function< (out s: std::string) -> void > = g_out&;
+ fg_move : std::function< (move s: std::string) -> void > = g_move&;
+ pg_in : * ( s: std::string) -> void = g_in&;
+ pg_inout: * (inout s: std::string) -> void = g_inout&;
+ pg_out : * (out s: std::string) -> void = g_out&;
+ pg_move : * (move s: std::string) -> void = g_move&;
+
+ frodo: std::string = "Frodo";
+ sam : std::string = "Sam";
+
+ // Test in param
+ fg_in(frodo);
+ pg_in(sam);
+
+ // Test inout
+ fg_inout(frodo);
+ pg_inout(sam);
+
+ // Test out
+ gandalf : std::string;
+ galadriel: std::string;
+ fg_out(out gandalf);
+ std::cout << "fg_out initialized gandalf to: (gandalf)$\n";
+ pg_out(out galadriel);
+ std::cout << "pg_out initialized galadriel to: (galadriel)$\n";
+ gandalf = "Gandalf";
+ galadriel = "Galadriel";
+
+ // Test move
+ fg_move(frodo); // last use, so (move frodo) is not required
+ pg_move(sam); // last use, so (move sam) is not required
+
+
+ // --- Tests for single anonymous returns
+ // Note: Not multiple named return values... function-type-ids
+ // are for Cpp1-style (single anonymous, possibly void) returns
+
+ fh_forward: std::function< (inout s: std::string) -> forward std::string > = h_forward&;
+ fh_out : std::function< ( s: std::string) -> std::string > = h_out&;
+ ph_forward: * (inout s: std::string) -> forward std::string = h_forward&;
+ ph_out : * ( s: std::string) -> std::string = h_out&;
+
+ ph_forward2: * A_h_forward = h_forward&;
+
+ // Test forward return
+ std::cout << "fh_forward returned: (fh_forward(gandalf))$\n";
+ std::cout << "ph_forward returned: (ph_forward(galadriel))$\n";
+ std::cout << "ph_forward2 returned: (ph_forward2(galadriel))$\n";
+
+ // Test out return
+ std::cout << "fh_out returned: (fh_out(gandalf))$\n";
+ std::cout << "ph_out returned: (ph_out(galadriel))$\n";
+
+
+ // --- Tests for function parameters
+ std::cout << "(f1(g&))$\n";
+ std::cout << "(f2(g&))$\n";
+
+
+}
diff --git a/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution
new file mode 100644
index 0000000000..08f16f663d
--- /dev/null
+++ b/regression-tests/test-results/clang-12-c++20/pure2-function-typeids.cpp.execution
@@ -0,0 +1,17 @@
+hello world!
+hello world!
+Come in, Frodo
+Come in, Sam
+Come in awhile, but take some biscuits on your way out, Frodo!
+Come in awhile, but take some biscuits on your way out, Sam!
+fg_out initialized gandalf to: A Powerful Mage
+pg_out initialized galadriel to: A Powerful Mage
+I hear you've moving, Frodo?
+I hear you've moving, Sam?
+Inout Gandalf ... fh_forward returned: Gandalf
+Inout Galadriel ... ph_forward returned: Galadriel
+Inout Galadriel ... ph_forward2 returned: Galadriel
+In Gandalf ... fh_out returned: yohoho
+In Galadriel ... ph_out returned: yohoho
+43
+44
diff --git a/regression-tests/test-results/clang-12-c++20/run-tests-clang-12.sh b/regression-tests/test-results/clang-12-c++20/run-tests-clang-12.sh
index d179a23909..36cd819582 100644
--- a/regression-tests/test-results/clang-12-c++20/run-tests-clang-12.sh
+++ b/regression-tests/test-results/clang-12-c++20/run-tests-clang-12.sh
@@ -20,5 +20,6 @@ do
fi
done
rm -f *.obj *.exp *.lib
+find . -type f -exec bash -c "[ ! -s \"{}\" ] && rm \"{}\"" \;
printf "\nDone: %s .cpp tests compiled\n" "$count"
printf "\n %s .cpp executables generated and run\n" "$exe_count"
diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution
new file mode 100644
index 0000000000..08f16f663d
--- /dev/null
+++ b/regression-tests/test-results/gcc-10-c++20/pure2-function-typeids.cpp.execution
@@ -0,0 +1,17 @@
+hello world!
+hello world!
+Come in, Frodo
+Come in, Sam
+Come in awhile, but take some biscuits on your way out, Frodo!
+Come in awhile, but take some biscuits on your way out, Sam!
+fg_out initialized gandalf to: A Powerful Mage
+pg_out initialized galadriel to: A Powerful Mage
+I hear you've moving, Frodo?
+I hear you've moving, Sam?
+Inout Gandalf ... fh_forward returned: Gandalf
+Inout Galadriel ... ph_forward returned: Galadriel
+Inout Galadriel ... ph_forward2 returned: Galadriel
+In Gandalf ... fh_out returned: yohoho
+In Galadriel ... ph_out returned: yohoho
+43
+44
diff --git a/regression-tests/test-results/gcc-10-c++20/run-tests-gcc-10.sh b/regression-tests/test-results/gcc-10-c++20/run-tests-gcc-10.sh
index 44b67ccd4a..098601c496 100644
--- a/regression-tests/test-results/gcc-10-c++20/run-tests-gcc-10.sh
+++ b/regression-tests/test-results/gcc-10-c++20/run-tests-gcc-10.sh
@@ -20,5 +20,6 @@ do
fi
done
rm -f *.obj *.exp *.lib
+find . -type f -exec bash -c "[ ! -s \"{}\" ] && rm \"{}\"" \;
printf "\nDone: %s .cpp tests compiled\n" "$count"
printf "\n %s .cpp executables generated and run\n" "$exe_count"
diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output
index 4ba9bb6e19..7fc683d2c6 100644
--- a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output
+++ b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output
@@ -1,41 +1,41 @@
In file included from mixed-bugfix-for-ufcs-non-local.cpp:6:
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
- 2100 | class finally_success
+ 2100 |
| ^
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
- 2137 | finally(finally&& that) noexcept
+ 2137 | ~finally() noexcept { f(); }
| ^
mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
- 2100 | class finally_success
+ 2100 |
| ^
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
- 2137 | finally(finally&& that) noexcept
+ 2137 | ~finally() noexcept { f(); }
| ^
mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
- 2100 | class finally_success
+ 2100 |
| ^
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
- 2137 | finally(finally&& that) noexcept
+ 2137 | ~finally() noexcept { f(); }
| ^
mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
- 2100 | class finally_success
+ 2100 |
| ^
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
- 2137 | finally(finally&& that) noexcept
+ 2137 | ~finally() noexcept { f(); }
| ^
mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
- 2100 | class finally_success
+ 2100 |
| ^
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
- 2137 | finally(finally&& that) noexcept
+ 2137 | ~finally() noexcept { f(); }
| ^
mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid
diff --git a/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution
new file mode 100644
index 0000000000..08f16f663d
--- /dev/null
+++ b/regression-tests/test-results/gcc-14-c++2b/pure2-function-typeids.cpp.execution
@@ -0,0 +1,17 @@
+hello world!
+hello world!
+Come in, Frodo
+Come in, Sam
+Come in awhile, but take some biscuits on your way out, Frodo!
+Come in awhile, but take some biscuits on your way out, Sam!
+fg_out initialized gandalf to: A Powerful Mage
+pg_out initialized galadriel to: A Powerful Mage
+I hear you've moving, Frodo?
+I hear you've moving, Sam?
+Inout Gandalf ... fh_forward returned: Gandalf
+Inout Galadriel ... ph_forward returned: Galadriel
+Inout Galadriel ... ph_forward2 returned: Galadriel
+In Gandalf ... fh_out returned: yohoho
+In Galadriel ... ph_out returned: yohoho
+43
+44
diff --git a/regression-tests/test-results/gcc-14-c++2b/run-tests-gcc-14.sh b/regression-tests/test-results/gcc-14-c++2b/run-tests-gcc-14.sh
index 07eaef6ea5..5627774436 100644
--- a/regression-tests/test-results/gcc-14-c++2b/run-tests-gcc-14.sh
+++ b/regression-tests/test-results/gcc-14-c++2b/run-tests-gcc-14.sh
@@ -20,5 +20,6 @@ do
fi
done
rm -f *.obj *.exp *.lib
+find . -type f -exec bash -c "[ ! -s \"{}\" ] && rm \"{}\"" \;
printf "\nDone: %s .cpp tests compiled\n" "$count"
printf "\n %s .cpp executables generated and run\n" "$exe_count"
diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution
new file mode 100644
index 0000000000..08f16f663d
--- /dev/null
+++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.execution
@@ -0,0 +1,17 @@
+hello world!
+hello world!
+Come in, Frodo
+Come in, Sam
+Come in awhile, but take some biscuits on your way out, Frodo!
+Come in awhile, but take some biscuits on your way out, Sam!
+fg_out initialized gandalf to: A Powerful Mage
+pg_out initialized galadriel to: A Powerful Mage
+I hear you've moving, Frodo?
+I hear you've moving, Sam?
+Inout Gandalf ... fh_forward returned: Gandalf
+Inout Galadriel ... ph_forward returned: Galadriel
+Inout Galadriel ... ph_forward2 returned: Galadriel
+In Gandalf ... fh_out returned: yohoho
+In Galadriel ... ph_out returned: yohoho
+43
+44
diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.output b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.output
new file mode 100644
index 0000000000..9629338025
--- /dev/null
+++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-function-typeids.cpp.output
@@ -0,0 +1 @@
+pure2-function-typeids.cpp
diff --git a/regression-tests/test-results/msvc-2022-c++latest/run-tests-msvc-2022.bat b/regression-tests/test-results/msvc-2022-c++latest/run-tests-msvc-2022.bat
index 270dfde121..6f002887a9 100644
--- a/regression-tests/test-results/msvc-2022-c++latest/run-tests-msvc-2022.bat
+++ b/regression-tests/test-results/msvc-2022-c++latest/run-tests-msvc-2022.bat
@@ -26,6 +26,7 @@ for %%f in (*.cpp) do (
)
)
del pure2-*.obj mixed-*.obj *.exp *.lib
+..\..\rm-empty-files.bat
echo.
echo Done: %count% .cpp tests compiled
echo.
diff --git a/regression-tests/test-results/pure2-function-typeids.cpp b/regression-tests/test-results/pure2-function-typeids.cpp
new file mode 100644
index 0000000000..bec31ef690
--- /dev/null
+++ b/regression-tests/test-results/pure2-function-typeids.cpp
@@ -0,0 +1,149 @@
+
+#define CPP2_IMPORT_STD Yes
+
+//=== Cpp2 type declarations ====================================================
+
+
+#include "cpp2util.h"
+
+#line 1 "pure2-function-typeids.cpp2"
+
+
+//=== Cpp2 type definitions and function declarations ===========================
+
+#line 1 "pure2-function-typeids.cpp2"
+
+// --- Scaffolding
+
+#line 4 "pure2-function-typeids.cpp2"
+auto f() -> void;
+
+auto g_in( cpp2::impl::in s) -> void;
+auto g_inout(std::string& s) -> void;
+auto g_out(cpp2::impl::out s) -> void;
+auto g_move(std::string&& s) -> void;
+
+[[nodiscard]] auto h_forward(std::string& s) -> std::string&;
+[[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string;
+
+[[nodiscard]] auto f1(cpp2::impl::in x)>> a) -> int;
+[[nodiscard]] auto f2(int(*a)(cpp2::impl::in x)) -> int;
+[[nodiscard]] auto g (cpp2::impl::in x) -> int;
+
+#line 19 "pure2-function-typeids.cpp2"
+// --- Tests for type aliases
+
+using A_h_forward = std::string&(std::string& s);
+
+#line 24 "pure2-function-typeids.cpp2"
+auto main() -> int;
+
+//=== Cpp2 function definitions =================================================
+
+#line 1 "pure2-function-typeids.cpp2"
+
+#line 4 "pure2-function-typeids.cpp2"
+auto f() -> void { std::cout << "hello world!\n"; }
+
+#line 6 "pure2-function-typeids.cpp2"
+auto g_in( cpp2::impl::in s) -> void { std::cout << "Come in, " + cpp2::to_string(s) + "\n"; }
+#line 7 "pure2-function-typeids.cpp2"
+auto g_inout(std::string& s) -> void { std::cout << "Come in awhile, but take some biscuits on your way out, " + cpp2::to_string(s) + "!\n"; }
+#line 8 "pure2-function-typeids.cpp2"
+auto g_out(cpp2::impl::out s) -> void { s.construct("A Powerful Mage"); }
+#line 9 "pure2-function-typeids.cpp2"
+auto g_move(std::string&& s) -> void { std::cout << "I hear you've moving, " + cpp2::to_string(cpp2::move(s)) + "?\n"; }
+
+#line 11 "pure2-function-typeids.cpp2"
+[[nodiscard]] auto h_forward(std::string& s) -> std::string&{std::cout << "Inout " + cpp2::to_string(s) + " ... "; return s; }
+#line 12 "pure2-function-typeids.cpp2"
+[[nodiscard]] auto h_out(cpp2::impl::in s) -> std::string{std::cout << "In " + cpp2::to_string(s) + " ... ";return "yohoho"; }
+
+#line 14 "pure2-function-typeids.cpp2"
+[[nodiscard]] auto f1(cpp2::impl::in x)>> a) -> int { return a(1); }
+#line 15 "pure2-function-typeids.cpp2"
+[[nodiscard]] auto f2(int(*a)(cpp2::impl::in x)) -> int { return a(2); }
+#line 16 "pure2-function-typeids.cpp2"
+[[nodiscard]] auto g (cpp2::impl::in x) -> int { return x + 42; }
+
+#line 24 "pure2-function-typeids.cpp2"
+auto main() -> int
+{
+ // --- Test basic/degenerate cases
+
+ // Test std::function< void() >
+ std::function ff {&f};
+ cpp2::move(ff)();
+
+ // Ordinary pointer to function, deduced (always worked)
+ void(*pf)() = &f;
+ cpp2::move(pf)();
+
+#line 37 "pure2-function-typeids.cpp2"
+ // --- Tests for parameters
+ // Note: Not forward parameters which imply a template...
+ // function type-ids are for single function signatures
+
+ std::function s)> fg_in {&g_in};
+ std::function fg_inout {&g_inout};
+ std::function s)> fg_out {&g_out};
+ std::function fg_move {&g_move};
+ void(*pg_in)(cpp2::impl::in< std::string> s) = &g_in;
+ void(*pg_inout)( std::string& s) = &g_inout;
+ void(*pg_out)(cpp2::impl::out s) = &g_out;
+ void(*pg_move)( std::string&& s) = &g_move;
+
+ std::string frodo {"Frodo"};
+ std::string sam {"Sam"};
+
+ // Test in param
+ cpp2::move(fg_in)(frodo);
+ cpp2::move(pg_in)(sam);
+
+ // Test inout
+ cpp2::move(fg_inout)(frodo);
+ cpp2::move(pg_inout)(sam);
+
+ // Test out
+ cpp2::impl::deferred_init gandalf;
+ cpp2::impl::deferred_init galadriel;
+ cpp2::move(fg_out)(cpp2::impl::out(&gandalf));
+ std::cout << "fg_out initialized gandalf to: " + cpp2::to_string(gandalf.value()) + "\n";
+ cpp2::move(pg_out)(cpp2::impl::out(&galadriel));
+ std::cout << "pg_out initialized galadriel to: " + cpp2::to_string(galadriel.value()) + "\n";
+ gandalf.value() = "Gandalf";
+ galadriel.value() = "Galadriel";
+
+ // Test move
+ cpp2::move(fg_move)(cpp2::move(frodo));// last use, so (move frodo) is not required
+ cpp2::move(pg_move)(cpp2::move(sam));// last use, so (move sam) is not required
+
+#line 76 "pure2-function-typeids.cpp2"
+ // --- Tests for single anonymous returns
+ // Note: Not multiple named return values... function-type-ids
+ // are for Cpp1-style (single anonymous, possibly void) returns
+
+ std::function fh_forward {&h_forward};
+ std::function s)> fh_out {&h_out};
+ std::string&(*ph_forward)( std::string& s) = &h_forward;
+ std::string(*ph_out)(cpp2::impl::in s) = &h_out;
+
+ A_h_forward* ph_forward2 {&h_forward};
+
+ // Test forward return
+ std::cout << "fh_forward returned: " + cpp2::to_string(cpp2::move(fh_forward)(gandalf.value())) + "\n";
+ std::cout << "ph_forward returned: " + cpp2::to_string(cpp2::move(ph_forward)(galadriel.value())) + "\n";
+ std::cout << "ph_forward2 returned: " + cpp2::to_string(cpp2::move(ph_forward2)(galadriel.value())) + "\n";
+
+ // Test out return
+ std::cout << "fh_out returned: " + cpp2::to_string(cpp2::move(fh_out)(cpp2::move(gandalf.value()))) + "\n";
+ std::cout << "ph_out returned: " + cpp2::to_string(cpp2::move(ph_out)(cpp2::move(galadriel.value()))) + "\n";
+
+#line 97 "pure2-function-typeids.cpp2"
+ // --- Tests for function parameters
+ std::cout << "" + cpp2::to_string(f1(&g)) + "\n";
+ std::cout << "" + cpp2::to_string(f2(&g)) + "\n";
+
+#line 102 "pure2-function-typeids.cpp2"
+}
+
diff --git a/regression-tests/test-results/pure2-function-typeids.cpp2.output b/regression-tests/test-results/pure2-function-typeids.cpp2.output
new file mode 100644
index 0000000000..ce9c9c2796
--- /dev/null
+++ b/regression-tests/test-results/pure2-function-typeids.cpp2.output
@@ -0,0 +1,2 @@
+pure2-function-typeids.cpp2... ok (all Cpp2, passes safety checks)
+
diff --git a/source/parse.h b/source/parse.h
index d01a7e0957..287747b0df 100644
--- a/source/parse.h
+++ b/source/parse.h
@@ -1325,26 +1325,39 @@ struct qualified_id_node
};
+struct function_type_node;
+
struct type_id_node
{
source_position pos;
std::vector pc_qualifiers;
- token const* address_of = {};
- token const* dereference_of = {};
- int dereference_cnt = {};
- token const* suspicious_initialization = {};
+ token const* address_of = {};
+ token const* dereference_of = {};
+ int dereference_cnt = {};
+ token const* suspicious_initialization = {};
- enum active { empty=0, qualified, unqualified, keyword };
+ enum active { empty=0, qualified, unqualified, function, keyword };
std::variant<
std::monostate,
std::unique_ptr,
std::unique_ptr,
+ std::unique_ptr,
token const*
> id;
std::unique_ptr constraint = {};
+ // Out-of-line definition of the dtor is necessary due to the forward-declared
+ // type(s) used in a std::unique_ptr as a member
+ ~type_id_node();
+
+ auto is_function_typeid() const
+ -> bool
+ {
+ return id.index() == function;
+ }
+
auto is_wildcard() const
-> bool
{
@@ -1416,6 +1429,8 @@ struct type_id_node
return {};
break;case unqualified:
return get(id)->get_token();
+ break;case function:
+ return {};
break;case keyword:
return get(id);
break;default:
@@ -1440,6 +1455,7 @@ struct type_id_node
}
try_visit(id, v, depth);
try_visit(id, v, depth);
+ try_visit(id, v, depth);
try_visit(id, v, depth);
if (constraint) {
constraint->visit(v, depth + 1);
@@ -2246,15 +2262,19 @@ auto compound_statement_node::visit(auto& v, int depth)
struct parameter_declaration_node
{
- source_position pos = {};
- passing_style pass = passing_style::in;
- int ordinal = 1;
+ parameter_declaration_list_node const* my_list;
+
+ source_position pos = {};
+ passing_style pass = passing_style::in;
+ int ordinal = 1;
enum class modifier { none=0, implicit, virtual_, override_, final_ };
modifier mod = modifier::none;
std::unique_ptr declaration;
+ parameter_declaration_node(parameter_declaration_list_node const* my) : my_list{my} { }
+
// API
//
auto has_name() const
@@ -2272,6 +2292,9 @@ struct parameter_declaration_node
return pass;
}
+ auto is_in_function_typeid() const
+ -> bool;
+
auto is_implicit() const
-> bool
{
@@ -2327,11 +2350,14 @@ struct parameter_declaration_node
struct parameter_declaration_list_node
{
- token const* open_paren = {};
- token const* close_paren = {};
+ token const* open_paren = {};
+ token const* close_paren = {};
+ bool in_function_typeid = false;
std::vector> parameters;
+ parameter_declaration_list_node(bool f = false) : in_function_typeid{f} { }
+
// API
//
auto ssize() const -> auto {
@@ -3112,6 +3138,8 @@ struct declaration_node
{ return type.index() == a_function; }
auto is_object () const -> bool
{ return type.index() == an_object; }
+ auto is_object_with_function_typeid() const -> bool
+ { return is_object() && std::get(type)->is_function_typeid(); }
auto is_base_object() const -> bool
{ return is_object() && has_name("this"); }
auto is_member_object() const -> bool
@@ -3884,6 +3912,13 @@ auto parameter_declaration_node::has_name(std::string_view s) const
}
+auto parameter_declaration_node::is_in_function_typeid() const
+ -> bool
+{
+ return my_list && my_list->in_function_typeid;
+}
+
+
auto function_type_node::first_parameter_name() const
-> std::string
{
@@ -3963,8 +3998,11 @@ auto function_type_node::is_defaultable() const
-> bool
{
if (
- my_decl->has_name("operator==")
- || my_decl->has_name("operator<=>")
+ my_decl
+ && (
+ my_decl->has_name("operator==")
+ || my_decl->has_name("operator<=>")
+ )
)
{
return true;
@@ -3982,7 +4020,7 @@ auto function_type_node::is_constructor() const
&& (*parameters)[0]->direction() == passing_style::out
)
{
- assert(my_decl->has_name("operator="));
+ assert(my_decl && my_decl->has_name("operator="));
return true;
}
return false;
@@ -4022,9 +4060,9 @@ auto function_type_node::is_move() const
auto function_type_node::is_swap() const
-> bool
{
- assert (my_decl);
if (
- my_decl->has_name("swap")
+ my_decl
+ && my_decl->has_name("swap")
&& (*parameters).ssize() == 2
&& (*parameters)[1]->has_name("that")
)
@@ -4101,7 +4139,8 @@ auto function_type_node::is_comparison() const
-> bool
{
if (
- (
+ my_decl
+ && (
my_decl->has_name("operator==")
|| my_decl->has_name("operator!=")
|| my_decl->has_name("operator<")
@@ -4122,8 +4161,11 @@ auto function_type_node::is_increment_or_decrement() const
-> bool
{
if (
- my_decl->has_name("operator++")
- || my_decl->has_name("operator--")
+ my_decl
+ && (
+ my_decl->has_name("operator++")
+ || my_decl->has_name("operator--")
+ )
)
{
return true;
@@ -4136,7 +4178,8 @@ auto function_type_node::is_compound_assignment() const
-> bool
{
if (
- (
+ my_decl
+ && (
my_decl->has_name("operator+=")
|| my_decl->has_name("operator-=")
|| my_decl->has_name("operator*=")
@@ -4163,7 +4206,8 @@ auto function_type_node::is_assignment() const
-> bool
{
if (
- my_decl->has_name("operator=")
+ my_decl
+ && my_decl->has_name("operator=")
&& (*parameters).ssize() > 1
&& (*parameters)[0]->has_name("this")
&& (*parameters)[0]->direction() == passing_style::inout
@@ -4211,7 +4255,8 @@ auto function_type_node::is_destructor() const
-> bool
{
if (
- my_decl->has_name("operator=")
+ my_decl
+ && my_decl->has_name("operator=")
&& (*parameters).ssize() == 1
&& (*parameters)[0]->has_name("this")
&& (*parameters)[0]->direction() == passing_style::move
@@ -4468,6 +4513,9 @@ struct translation_unit_node
};
// Definitions of out-of-line dtors for nodes with unique_ptr members of forward-declared types
+
+type_id_node::~type_id_node() = default;
+
primary_expression_node::~primary_expression_node() = default;
prefix_expression_node::~prefix_expression_node() = default;
@@ -4848,6 +4896,7 @@ auto pretty_print_visualize(type_id_node const& n, int indent)
if (n.id.index() == type_id_node::empty) { ret += "_"; }
ret += try_pretty_print_visualize(n.id, indent);
ret += try_pretty_print_visualize(n.id, indent);
+ ret += try_pretty_print_visualize(n.id, indent);
ret += try_pretty_print_visualize(n.id, indent);
if (n.constraint) {
@@ -5852,6 +5901,9 @@ class parser
// || curr().type() == lexeme::LeftBrace
)
{
+ // Remember current position, because we may need to backtrack
+ auto start_pos = pos;
+
bool inside_initializer = (
peek(-1) && peek(-1)->type() == lexeme::Assignment
);
@@ -5859,17 +5911,23 @@ class parser
auto close = close_paren_type(open_paren->type());
auto close_text = [&] () -> std::string { if (close == lexeme::RightParen) { return ")"; } return "}"; }();
next();
+
auto expr_list = expression_list(open_paren, lexeme::RightParen, inside_initializer);
if (!expr_list) {
- error("unexpected text - ( is not followed by an expression-list");
+ //error("unexpected text - ( is not followed by an expression-list");
+ pos = start_pos; // backtrack
return {};
}
+
if (curr().type() != close_paren_type(open_paren->type())) {
- error("unexpected text - expression-list is not terminated by " + close_text);
+ //error("unexpected text - expression-list is not terminated by " + close_text);
+ pos = start_pos; // backtrack
return {};
}
+
expr_list->close_paren = &curr();
next();
+
if (
curr().type() != lexeme::Semicolon
&& curr().type() != lexeme::RightParen
@@ -5882,6 +5940,7 @@ class parser
n->expression_list_is_fold_expression = expr_list->is_fold_expression();
expr_list->default_initializer =
is_inside_call_expr && std::empty(expr_list->expressions);
+
n->expr = std::move(expr_list);
return n;
}
@@ -6530,7 +6589,7 @@ class parser
&& curr().type() == lexeme::Arrow
)
{
- error("'->' is not Cpp2 deference syntax - write '*.' instead");
+ error("'->' is not Cpp2 dereference syntax - write '*.' instead");
return {};
}
}
@@ -6609,6 +6668,7 @@ class parser
//G type-id:
//G type-qualifier-seq? qualified-id is-type-constraint?
//G type-qualifier-seq? unqualified-id is-type-constraint?
+ //G type-qualifier-seq? function-type is-type-constraint?
//G
//G type-qualifier-seq:
//G type-qualifier
@@ -6620,7 +6680,8 @@ class parser
//G
auto type_id(
bool allow_omitting_type_name = false,
- bool allow_constraint = false
+ bool allow_constraint = false,
+ bool allow_function_type = false
)
-> std::unique_ptr
{
@@ -6657,9 +6718,19 @@ class parser
n->id = std::move(id);
assert (n->id.index() == type_id_node::unqualified);
}
+ else if (std::unique_ptr id = {};
+ allow_function_type
+ && (id = function_type({})) != nullptr
+ )
+ {
+ n->pos = id->position();
+ n->id = std::move(id);
+ assert (n->id.index() == type_id_node::function);
+ }
else if (!allow_omitting_type_name) {
return {};
}
+
if (curr().type() == lexeme::Multiply) {
error("'T*' is not a valid Cpp2 type; use '*T' for a pointer instead", false);
return {};
@@ -6826,12 +6897,17 @@ class parser
auto term = template_argument{};
do {
- // If it doesn't start with * or const (which can only be a type id),
+ // If it doesn't start with * or const or ()-> (which can only be a type id),
// try parsing it as an expression
if (auto e = [&]{
if (
curr().type() == lexeme::Multiply // '*'
|| curr() == "const" // 'const'
+ || (
+ curr().type() == lexeme::LeftParen
+ && peek(1) && peek(1)->type() == lexeme::RightParen
+ && peek(2) && peek(2)->type() == lexeme::Arrow
+ )
)
{
return decltype(expression()){};
@@ -6844,7 +6920,7 @@ class parser
}
// Else try parsing it as a type id
- else if (auto i = type_id()) {
+ else if (auto i = type_id(false, false, true)) {
term.arg = std::move(i);
}
@@ -7360,7 +7436,7 @@ class parser
}
next(2); // eat 'do' and '('
- n->parameter = parameter_declaration(false, false, false);
+ n->parameter = parameter_declaration(nullptr, false, false, false);
if (!n->parameter) {
error("'for range do (' must be followed by a parameter declaration", false, source_position{}, true);
return {};
@@ -7866,10 +7942,11 @@ class parser
//G 'final'
//G
auto parameter_declaration(
- bool is_returns = false,
- bool is_named = true,
- bool is_template = true,
- bool is_statement = false
+ parameter_declaration_list_node const* my_list,
+ bool is_returns = false,
+ bool is_named = true,
+ bool is_template = true,
+ bool is_statement = false
)
-> std::unique_ptr
{
@@ -7877,7 +7954,7 @@ class parser
// a parenthesized expression statement, not a statement parameter list
auto start_pos = pos;
- auto n = std::make_unique();
+ auto n = std::make_unique(my_list);
n->pass =
is_returns ? passing_style::out :
passing_style::in;
@@ -8051,10 +8128,11 @@ class parser
//G parameter-declaration-seq ',' parameter-declaration
//G
auto parameter_declaration_list(
- bool is_returns = false,
- bool is_named = true,
- bool is_template = false,
- bool is_statement = false
+ bool is_returns = false,
+ bool is_named = true,
+ bool is_template = false,
+ bool is_statement = false,
+ bool is_function_typeid = false
)
-> std::unique_ptr
{
@@ -8075,15 +8153,15 @@ class parser
return {};
}
- auto n = std::make_unique();
+ auto n = std::make_unique(is_function_typeid);
n->open_paren = &curr();
next();
- auto param = std::make_unique();
+ auto param = std::unique_ptr();
auto count = 1;
- while ((param = parameter_declaration(is_returns, is_named, is_template, is_statement)) != nullptr)
+ while ((param = parameter_declaration(n.get(), is_returns, is_named, is_template, is_statement)) != nullptr)
{
param->ordinal = count;
++count;
@@ -8257,7 +8335,7 @@ class parser
//G contract-seq contract
//G
auto function_type(
- declaration_node* my_decl,
+ declaration_node* my_decl, // if null, this is a type-id not a declaration
bool is_named = true
)
-> std::unique_ptr
@@ -8265,7 +8343,7 @@ class parser
auto n = std::make_unique( my_decl );
// Parameters
- auto parameters = parameter_declaration_list(false, is_named, false);
+ auto parameters = parameter_declaration_list(false, is_named, false, false, my_decl == nullptr);
if (!parameters) {
return {};
}
@@ -8292,10 +8370,12 @@ class parser
}
- // If we're not at a '->' or 'requires' or contract and what follows is
+ // If we're in an actual function declaration (not just a function type-id),
+ // and are not at a '->' or 'requires' or contract and what follows is
// an expression, this is a ":(params) expr" shorthand function syntax
if (
- curr().type() != lexeme::Arrow
+ my_decl
+ && curr().type() != lexeme::Arrow
&& curr() != "requires"
&& (curr() != "pre" && curr() != "post")
)
@@ -8366,6 +8446,11 @@ class parser
else if (auto returns_list = parameter_declaration_list(true, is_named))
{
+ if (!my_decl) {
+ error("a function type alias with multiple/named return values is not yet supported");
+ return {};
+ }
+
if (std::ssize(returns_list->parameters) < 1) {
error("an explicit return value list cannot be empty");
return {};
@@ -8384,6 +8469,11 @@ class parser
// Pre/post conditions
while (auto c = contract())
{
+ if (!my_decl) {
+ error("a function type alias with contracts is not yet supported");
+ return {};
+ }
+
if (
*c->kind != "pre"
&& *c->kind != "post"
@@ -8638,7 +8728,7 @@ class parser
}
// Or just a (possibly empty == deduced) type-id
- else if (auto t = type_id(true, !is_template_parameter))
+ else if (auto t = type_id(true, !is_template_parameter, true))
{
if (
t->get_token()
@@ -9095,7 +9185,7 @@ class parser
// Type alias
if (*a->type == "type")
{
- auto t = type_id();
+ auto t = type_id(false, false, true);
if (!t) {
errors.emplace_back(
curr().position(),
@@ -9682,9 +9772,10 @@ class parse_tree_printer : printing_visitor
assert( n.declaration );
}
- auto start(parameter_declaration_list_node const&, int indent) -> void
+ auto start(parameter_declaration_list_node const& n, int indent) -> void
{
o << pre(indent) << "parameter-declaration-list\n";
+ o << pre(indent+1) << "in_function_type " << std::boolalpha << n.in_function_typeid << "\n";
}
auto start(translation_unit_node const&, int indent) -> void
diff --git a/source/sema.h b/source/sema.h
index 27169fee00..bc4ec0d968 100644
--- a/source/sema.h
+++ b/source/sema.h
@@ -2069,8 +2069,11 @@ class sema
// An increment/decrement function must have a single 'inout' parameter,
// and if it's a member flag it if we know the type is not copyable
if (
- n.my_decl->has_name("operator++")
- || n.my_decl->has_name("operator--")
+ n.my_decl
+ && (
+ n.my_decl->has_name("operator++")
+ || n.my_decl->has_name("operator--")
+ )
)
{
if (
@@ -2302,10 +2305,12 @@ class sema
inside_next_expression = false;
}
- auto start(parameter_declaration_list_node const&, int) -> void
+ auto start(parameter_declaration_list_node const& n, int) -> void
{
inside_parameter_list = true;
- push_lifetime_scope();
+ if (!n.in_function_typeid) {
+ push_lifetime_scope();
+ }
}
auto end(parameter_declaration_list_node const&, int) -> void
@@ -2325,6 +2330,11 @@ class sema
auto start(parameter_declaration_node const& n, int) -> void
{
+ // Ignore parameters in function type-ids
+ if (n.is_in_function_typeid()) {
+ return;
+ }
+
if (
// If it's an 'out' parameter
(
@@ -2439,6 +2449,7 @@ class sema
}
if (
+ // Skip aliases
!n.is_alias()
// Skip type scope (member) variables
&& !(n.parent_is_type() && n.is_object())
@@ -2452,6 +2463,8 @@ class sema
!inside_parameter_list
|| inside_out_parameter
)
+ // Skip local variables that are pointers/etc. to functions
+ //&& !n.is_object_with_function_typeid()
)
{
symbols.emplace_back( scope_depth, declaration_sym( false, &n, nullptr, nullptr, inside_out_parameter, false, inside_returns_list ) );
diff --git a/source/to_cpp1.h b/source/to_cpp1.h
index 345283cc91..765b4e2be3 100644
--- a/source/to_cpp1.h
+++ b/source/to_cpp1.h
@@ -1892,7 +1892,8 @@ class cppfront
//
auto emit(
type_id_node const& n,
- source_position pos = {}
+ source_position pos = {},
+ std::string identifier = {}
)
-> void
{ STACKINSTR
@@ -1900,6 +1901,22 @@ class cppfront
pos = n.position();
}
+ // Handle function types
+ if (n.is_function_typeid()) {
+ // If identifier is nonempty, we're doing a local variable with a (pointer to)
+ // function typeid, so stick in the pointers here for inside-out Cpp1 declarations
+ if (!identifier.empty()) {
+ for (auto q: n.pc_qualifiers) {
+ if (*q == "const") { identifier = " " + identifier; }
+ identifier = q->as_string_view() + identifier;
+ }
+ identifier = "(" + identifier + ")";
+ }
+ emit( *std::get(n.id), false, false, {}, false, identifier );
+ return;
+ }
+
+ // Handle all other types
if (n.is_wildcard()) {
printer.print_cpp2("auto", pos);
}
@@ -4417,7 +4434,8 @@ class cppfront
//-----------------------------------------------------------------------
// Else handle ordinary parameters
- auto param_type = print_to_string(type_id);
+ assert(n.declaration->identifier);
+ auto param_type = print_to_string(type_id, source_position{}, print_to_string(*n.declaration->identifier));
// If there are template parameters on this function or its enclosing
// type, see if this parameter's name is an unqualified-id with a
@@ -4595,7 +4613,7 @@ class cppfront
if (is_returns) {
printer.print_extra( " " + identifier );
}
- else {
+ else if (!n.declaration->is_object_with_function_typeid()) {
printer.print_cpp2( " ", identifier_pos );
if (n.declaration->is_variadic)
{
@@ -4800,7 +4818,8 @@ class cppfront
bool is_main = false,
bool is_ctor_or_dtor = false,
std::string suffix1 = {},
- bool generating_postfix_inc_dec = false
+ bool generating_postfix_inc_dec = false,
+ std::string identifier = {}
)
-> void
{ STACKINSTR
@@ -4808,6 +4827,64 @@ class cppfront
return;
}
+ // Common parts we may use in different orders
+ //
+ auto handle_default_return_type = [&]{
+ if (is_main)
+ {
+ printer.print_cpp2( " -> int", n.position() );
+ }
+ else if(!is_ctor_or_dtor)
+ {
+ printer.print_cpp2( " -> void", n.position() );
+ }
+ };
+
+ auto handle_single_anon_return_type = [&]{
+ auto is_type_scope_function_with_in_this =
+ n.my_decl
+ && n.my_decl->parent_is_type()
+ && n.parameters->ssize() > 0
+ && (*n.parameters)[0]->direction() == passing_style::in
+ ;
+
+ auto& r = std::get(n.returns);
+ assert(r.type);
+
+ auto return_type = print_to_string(*r.type);
+
+ if (r.pass == passing_style::forward) {
+ if (r.type->is_wildcard()) {
+ printer.print_cpp2( "auto&&", n.position() );
+ }
+ else {
+ printer.print_cpp2( return_type, n.position() );
+ if (is_type_scope_function_with_in_this) {
+ printer.print_cpp2( " const&", n.position() );
+ }
+ else if (!generating_postfix_inc_dec) {
+ printer.print_cpp2( "&", n.position() );
+ }
+ }
+ }
+ else {
+ printer.print_cpp2( return_type, n.position() );
+ }
+ };
+
+
+ // If this is not part of a declaration, just a function type-id,
+ // the return type comes first
+ if (!n.my_decl) {
+ if (n.returns.index() == function_type_node::empty) {
+ handle_default_return_type();
+ }
+ else if (n.returns.index() == function_type_node::id) {
+ handle_single_anon_return_type();
+ }
+ printer.print_cpp2( identifier, n.position() );
+ }
+
if (
is_main
&& n.parameters->parameters.size() > 0
@@ -4826,7 +4903,10 @@ class cppfront
}
// For an anonymous function, the emitted lambda is 'constexpr' or 'mutable'
- if (!n.my_decl->has_name())
+ if (
+ n.my_decl
+ && !n.my_decl->has_name()
+ )
{
if (n.my_decl->is_constexpr) {
// The current design path we're trying out is for all '==' functions to be
@@ -4851,7 +4931,10 @@ class cppfront
n.is_move()
|| n.is_swap()
|| n.is_destructor()
- || generating_move_from == n.my_decl
+ || (
+ n.my_decl
+ && generating_move_from == n.my_decl
+ )
)
{
printer.print_cpp2( " noexcept", n.position() );
@@ -4862,7 +4945,10 @@ class cppfront
// Handle a special member function
if (
n.is_assignment()
- || generating_assignment_from == n.my_decl
+ || (
+ n.my_decl
+ && generating_assignment_from == n.my_decl
+ )
)
{
assert(
@@ -4875,59 +4961,27 @@ class cppfront
);
}
- // Otherwise, handle a default return type
- else if (n.returns.index() == function_type_node::empty)
+ // If this is a declaration, emit the trailing return type
+ else if (n.my_decl)
{
- if (is_main)
- {
- printer.print_cpp2( " -> int", n.position() );
- }
- else if(!is_ctor_or_dtor)
- {
- printer.print_cpp2( " -> void", n.position() );
+ // Otherwise, handle a default return type
+ if (n.returns.index() == function_type_node::empty) {
+ handle_default_return_type();
}
- }
- // Otherwise, handle a single anonymous return type
- else if (n.returns.index() == function_type_node::id)
- {
- auto is_type_scope_function_with_in_this =
- n.my_decl->parent_is_type()
- && n.parameters->ssize() > 0
- && (*n.parameters)[0]->direction() == passing_style::in
- ;
-
- printer.print_cpp2( " -> ", n.position() );
- auto& r = std::get(n.returns);
- assert(r.type);
-
- auto return_type = print_to_string(*r.type);
-
- if (r.pass == passing_style::forward) {
- if (r.type->is_wildcard()) {
- printer.print_cpp2( "auto&&", n.position() );
- }
- else {
- printer.print_cpp2( return_type, n.position() );
- if (is_type_scope_function_with_in_this) {
- printer.print_cpp2( " const&", n.position() );
- }
- else if (!generating_postfix_inc_dec) {
- printer.print_cpp2( "&", n.position() );
- }
- }
+ // Otherwise, handle a single anonymous return type
+ else if (n.returns.index() == function_type_node::id) {
+ printer.print_cpp2( " -> ", n.position() );
+ handle_single_anon_return_type();
}
+
+ // Otherwise, handle multiple/named returns
else {
- printer.print_cpp2( return_type, n.position() );
+ printer.print_cpp2( " -> ", n.position() );
+ assert (n.my_decl);
+ printer.print_cpp2( multi_return_type_name(*n.my_decl), n.position());
}
}
-
- // Otherwise, handle multiple/named returns
- else {
- printer.print_cpp2( " -> ", n.position() );
- assert (n.my_decl);
- printer.print_cpp2( multi_return_type_name(*n.my_decl), n.position());
- }
}
@@ -6747,6 +6801,8 @@ class cppfront
printer.print_cpp2( "extern ", n.position() );
}
+ assert(n.identifier);
+
// Emit "auto" for deduced types (of course)
if (type->is_wildcard()) {
assert(n.initializer);
@@ -6772,10 +6828,10 @@ class cppfront
return;
}
}
- printer.preempt_position_push(n.position());
- emit( *type );
+ printer.preempt_position_push(n.position());
+ emit( *type, {}, print_to_string(*n.identifier) );
printer.preempt_position_pop();
- // one pointer is enough for now, pointer-to-function fun can be later
+
if (
!n.initializer
&& n.parent_is_function()
@@ -6786,14 +6842,19 @@ class cppfront
}
printer.print_cpp2( " ", n.position());
- assert(n.identifier);
// If this is anonymous object (named "_"), generate a unique name
if (n.has_name("_")) {
if (n.has_wildcard_type()) {
errors.emplace_back(
n.identifier->position(),
- "an object can have an anonymous name or an anonymous type, but not both at the same time (rationale: if '_ := f();' were allowed to keep the returned object alive, that syntax would be dangerously close to '_ = f();' to discard the returned object, and such importantly opposite meanings deserve more than a one-character typo distance; and explicit discarding gets the nice syntax because it's likely more common)"
+ "an object can have an anonymous name or an anonymous type, "
+ "but not both at the same time (rationale: if '_ := f();' were "
+ "allowed to keep the returned object alive, that syntax would "
+ "be dangerously close to '_ = f();' to discard the returned "
+ "object, and such importantly opposite meanings deserve more "
+ "than a one-character typo distance; and explicit discarding "
+ "gets the nice syntax because it's likely more common)"
);
return;
}
@@ -6815,7 +6876,7 @@ class cppfront
n.identifier->position()
);
}
- else {
+ else if (!n.is_object_with_function_typeid()) {
emit(*n.identifier);
}
@@ -6833,9 +6894,15 @@ class cppfront
if (n.initializer)
{
printer.add_pad_in_this_line(-100);
- if (type->is_concept()) {
+ if (
+ type->is_concept()
+ || type->is_function_typeid()
+ )
+ {
printer.print_cpp2( " = ", n.position() );
- } else {
+ }
+ else
+ {
printer.print_cpp2( " {", n.position() );
}
@@ -6844,7 +6911,11 @@ class cppfront
emit( *n.initializer, false );
pop_need_expression_list_parens();
- if (!type->is_concept()) {
+ if (
+ !type->is_concept()
+ && !type->is_function_typeid()
+ )
+ {
printer.print_cpp2( "}", n.position() );
}
}