Support C++17 fold expressions
Unary left fold expressions are not supported currently. Also the same
restrictions that apply to other expressions apply here too.
Fixes #2868
diff --git a/CHANGES.current b/CHANGES.current
index 4280ded..dff98f2 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -8,6 +8,9 @@
===========================
2024-05-15: olly
+ #2868 Support C++17 fold expressions.
+
+2024-05-15: olly
#2876 Report error if parser stack depth exceeded. Previously SWIG
would quietly exit with status 0 in this situation.
diff --git a/Doc/Manual/CPlusPlus17.html b/Doc/Manual/CPlusPlus17.html
index a234666..b07a410 100644
--- a/Doc/Manual/CPlusPlus17.html
+++ b/Doc/Manual/CPlusPlus17.html
@@ -17,6 +17,7 @@
<li><a href="#CPlusPlus17_nested_namespaces">Nested namespace definitions</a>
<li><a href="#CPlusPlus17_u8_char_literals">UTF-8 character literals</a>
<li><a href="#CPlusPlus17_hexadecimal_floating_literals">Hexadecimal floating literals</a>
+<li><a href="#CPlusPlus17_fold_expressions">Fold expressions</a>
</ul>
<li><a href="#CPlusPlus17_standard_library_changes">Standard library changes</a>
</ul>
@@ -102,6 +103,16 @@
</pre>
</div>
+<H3><a name="CPlusPlus17_fold_expressions">9.2.4 Fold expressions</a></H3>
+
+
+<p>
+C++17 added template fold expressions. SWIG 4.3.0 and later support
+parsing these with a few restrictions. Unary left fold expressions are
+not supported currently. Also the same restrictions that apply to other
+expressions apply here too.
+</p>
+
<H2><a name="CPlusPlus17_standard_library_changes">9.3 Standard library changes</a></H2>
diff --git a/Examples/test-suite/cpp17_enable_if_t.i b/Examples/test-suite/cpp17_enable_if_t.i
index a6695bc..88d3d6f 100644
--- a/Examples/test-suite/cpp17_enable_if_t.i
+++ b/Examples/test-suite/cpp17_enable_if_t.i
@@ -87,3 +87,32 @@
// xxx<TestStruct>(); // compilation error
}
%}
+
+// Check fold expressions parse (#2868):
+#define FOLD_EXPR_TEST(OP, FUNC) \
+ template< \
+ typename... Ts, \
+ typename R = typename std::common_type_t<Ts...>, \
+ std::enable_if_t< \
+ (std::is_same_v<typename std::decay_t<Ts>,HalfInt> OP ...) \
+ && (std::is_constructible_v<HalfInt, R> \
+ || std::is_convertible_v<R, HalfInt>) \
+ >* = nullptr \
+ > \
+ constexpr inline R FUNC(const Ts&... t) { return std::min(static_cast<R>(t)...); }
+FOLD_EXPR_TEST(+, f1)
+FOLD_EXPR_TEST(-, f2)
+FOLD_EXPR_TEST(*, f3)
+FOLD_EXPR_TEST(/, f4)
+FOLD_EXPR_TEST(%, f5)
+FOLD_EXPR_TEST(&, f6)
+FOLD_EXPR_TEST(|, f7)
+FOLD_EXPR_TEST(^, f8)
+FOLD_EXPR_TEST(<<, f9)
+FOLD_EXPR_TEST(>>, f10)
+FOLD_EXPR_TEST(&&, f11)
+FOLD_EXPR_TEST(||, f12)
+FOLD_EXPR_TEST(==, f13)
+FOLD_EXPR_TEST(!=, f14)
+FOLD_EXPR_TEST(>=, f15)
+FOLD_EXPR_TEST(<=, f16)
diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y
index 1ce7d3c..fcd146f 100644
--- a/Source/CParse/parser.y
+++ b/Source/CParse/parser.y
@@ -6907,6 +6907,100 @@
$$.val = NewStringf("%s <= %s", COMPOUND_EXPR_VAL($lhs), COMPOUND_EXPR_VAL($rhs));
$$.type = cparse_cplusplus ? T_BOOL : T_INT;
}
+
+ // C++17 fold expressions.
+ //
+ // We don't handle unary left fold currently, since the obvious
+ // approach introduces shift/reduce conflicts. (Binary folds
+ // should be handled by composition of expressions.)
+ //
+ // Fold expressions using the following operators are not
+ // currently handled (because we don't actually seem to handle
+ // these operators in expressions at all!):
+ //
+ // = += -= *= /= %= ^= &= |= <<= >>= , .* ->*.
+ | expr[lhs] PLUS ELLIPSIS {
+ $$.val = NewStringf("%s+...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] MINUS ELLIPSIS {
+ $$.val = NewStringf("%s-...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] STAR ELLIPSIS {
+ $$.val = NewStringf("%s*...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] SLASH ELLIPSIS {
+ $$.val = NewStringf("%s/...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] MODULO ELLIPSIS {
+ $$.val = NewStringf("%s%%...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] AND ELLIPSIS {
+ $$.val = NewStringf("%s&...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] OR ELLIPSIS {
+ $$.val = NewStringf("%s|...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] XOR ELLIPSIS {
+ $$.val = NewStringf("%s^...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] LSHIFT ELLIPSIS {
+ $$.val = NewStringf("%s << ...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] RSHIFT ELLIPSIS {
+ $$.val = NewStringf("%s >> ...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = promote_type($lhs.type);
+ }
+ | expr[lhs] LAND ELLIPSIS {
+ $$.val = NewStringf("%s&&...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr[lhs] LOR ELLIPSIS {
+ $$.val = NewStringf("%s||...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr[lhs] EQUALTO ELLIPSIS {
+ $$.val = NewStringf("%s==...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr[lhs] NOTEQUALTO ELLIPSIS {
+ $$.val = NewStringf("%s!=...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ /* Trying to parse `>` in the general case results in conflicts
+ * in the parser, but all user-reported cases are actually inside
+ * parentheses and we can handle that case.
+ */
+ | LPAREN expr[lhs] GREATERTHAN ELLIPSIS RPAREN {
+ $$.val = NewStringf("(%s > ...)", COMPOUND_EXPR_VAL($lhs));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ /* Similarly for `<` except trying to handle exprcompound on the
+ * left side gives a shift/reduce conflict, so also restrict
+ * handling to non-compound subexpressions there. Again this
+ * covers all user-reported cases.
+ */
+ | LPAREN exprsimple[lhs] LESSTHAN ELLIPSIS RPAREN {
+ $$.val = NewStringf("(%s < %s)", COMPOUND_EXPR_VAL($lhs));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr[lhs] GREATERTHANOREQUALTO ELLIPSIS {
+ $$.val = NewStringf("%s >= ...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr[lhs] LESSTHANOREQUALTO ELLIPSIS {
+ $$.val = NewStringf("%s <= ...", COMPOUND_EXPR_VAL($lhs));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+
| expr[lhs] LESSEQUALGREATER expr[rhs] {
$$.val = NewStringf("%s <=> %s", COMPOUND_EXPR_VAL($lhs), COMPOUND_EXPR_VAL($rhs));
/* `<=>` returns one of `std::strong_ordering`,