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
This commit is contained in:
Olly Betts 2024-05-15 11:10:00 +12:00
parent 6fed8d96c1
commit 7c2b245cea
4 changed files with 137 additions and 0 deletions

View File

@ -7,6 +7,9 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.3.0 (in progress)
===========================
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.

View File

@ -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 @@ double f = 0xF.68p2;
</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>

View File

@ -87,3 +87,32 @@ void tester2() {
// 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)

View File

@ -6907,6 +6907,100 @@ exprcompound : expr[lhs] PLUS expr[rhs] {
$$.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`,