From f2de21eb8382f755299bea04b18e2fd8648fddc1 Mon Sep 17 00:00:00 2001 From: Olly Betts Date: Wed, 29 Sep 2021 17:51:02 +1300 Subject: [PATCH] Parse common cases of `<` and `>` comparisons Adding full support for these in expressions seems hard to do without introducing conflicts into the parser grammar, but in fact all reported cases have had parentheses around the comparison and we can support that with a few restrictions on the left side of `<`. Fixes #80 and #635. Also https://sourceforge.net/p/swig/bugs/1139/ --- CHANGES.current | 9 +++++ Examples/test-suite/common.mk | 3 +- Examples/test-suite/constant_expr.i | 16 ++++++--- Examples/test-suite/constant_expr_c.i | 35 +++++++++++++++++++ .../csharp/preproc_constants_c_runme.cs | 2 ++ .../csharp/preproc_constants_runme.cs | 2 ++ .../d/preproc_constants_c_runme.1.d | 2 ++ .../d/preproc_constants_c_runme.2.d | 2 ++ .../test-suite/d/preproc_constants_runme.1.d | 2 ++ .../test-suite/d/preproc_constants_runme.2.d | 2 ++ .../php/preproc_constants_c_runme.php | 2 ++ Examples/test-suite/preproc_constants.i | 6 ++-- RELEASENOTES | 2 ++ Source/CParse/parser.y | 33 +++++++++++------ 14 files changed, 98 insertions(+), 20 deletions(-) create mode 100644 Examples/test-suite/constant_expr_c.i diff --git a/CHANGES.current b/CHANGES.current index 59257dc09..028adcf74 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,15 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.1.0 (in progress) =========================== +2022-01-25: olly + #80 #635 https://sourceforge.net/p/swig/bugs/1139/ + Add support for parsing common cases of `<` and `>` comparisons + in constant expressions. Adding full support for these seems hard + to do without introducing conflicts into the parser grammar, but in + fact all reported cases have had parentheses around the comparison + and we can support that with a few restrictions on the left side of + `<`. + 2022-01-25: wsfulton New warning 327 for extern templates, eg: diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index 5a44980cc..6574c4549 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -141,6 +141,7 @@ CPP_TEST_CASES += \ compactdefaultargs \ const_const_2 \ constant_directive \ + constant_expr \ constant_pointers \ constover \ constructor_copy \ @@ -689,7 +690,7 @@ C_TEST_CASES += \ c_delete_function \ char_constant \ const_const \ - constant_expr \ + constant_expr_c \ default_args_c \ empty_c \ enums \ diff --git a/Examples/test-suite/constant_expr.i b/Examples/test-suite/constant_expr.i index 8e5c8aee6..7cb529f8a 100644 --- a/Examples/test-suite/constant_expr.i +++ b/Examples/test-suite/constant_expr.i @@ -1,11 +1,17 @@ %module constant_expr; -/* Tests of constant expressions. */ +/* Tests of constant expressions (C++ version). */ + +%include "constant_expr_c.i" %inline %{ -/* % didn't work in SWIG 1.3.40 and earlier. */ -const int X = 123%7; -#define FOO 12 % 9 -double d_array[12 % 9]; +// Testcase from https://sourceforge.net/p/swig/bugs/1139/ +template +struct SizeInfo { +enum { +isLarge = (sizeof(Tp)>sizeof(void*)), +isPointer = false +}; +}; %} diff --git a/Examples/test-suite/constant_expr_c.i b/Examples/test-suite/constant_expr_c.i new file mode 100644 index 000000000..b7431c1f1 --- /dev/null +++ b/Examples/test-suite/constant_expr_c.i @@ -0,0 +1,35 @@ +%module constant_expr_c; +/* Tests of constant expressions (C version). */ + +%inline %{ + +/* % didn't work in SWIG 1.3.40 and earlier. */ +const int X = 123%7; +#define FOO 12 % 9 +double d_array[12 % 9]; + +/* `<` and `>` in constant expressions caused parse errors before SWIG 4.1.0. + * They're now supported if inside parentheses (and with some restrictions + * on the LHS of `<`. + */ + +// Testcase from https://github.com/swig/swig/issues/635 +#define TEST_A 1 +#define TEST_B 2 +#define TEST_C (TEST_A < TEST_B) +#define TEST_D (TEST_A > TEST_B) +// These have been supported since 1.3.41. +#define TEST_E (TEST_A <= TEST_B) +#define TEST_F (TEST_A >= TEST_B) +// For completeness +#define TEST_G (TEST_A == TEST_B) +#define TEST_H (TEST_A != TEST_B) + +// No warning +#if (TEST_A < TEST_B) +#define TEST_I 1 +#else +#define TEST_I 0 +#endif + +%} diff --git a/Examples/test-suite/csharp/preproc_constants_c_runme.cs b/Examples/test-suite/csharp/preproc_constants_c_runme.cs index 5966d5266..8cdb705c9 100644 --- a/Examples/test-suite/csharp/preproc_constants_c_runme.cs +++ b/Examples/test-suite/csharp/preproc_constants_c_runme.cs @@ -51,6 +51,8 @@ public class runme { assert( typeof(int) == preproc_constants_c.EXPR_MINUS.GetType() ); assert( typeof(int) == preproc_constants_c.EXPR_LSHIFT.GetType() ); assert( typeof(int) == preproc_constants_c.EXPR_RSHIFT.GetType() ); + assert( typeof(int) == preproc_constants_c.EXPR_LT.GetType() ); + assert( typeof(int) == preproc_constants_c.EXPR_GT.GetType() ); assert( typeof(int) == preproc_constants_c.EXPR_LTE.GetType() ); assert( typeof(int) == preproc_constants_c.EXPR_GTE.GetType() ); assert( typeof(int) == preproc_constants_c.EXPR_INEQUALITY.GetType() ); diff --git a/Examples/test-suite/csharp/preproc_constants_runme.cs b/Examples/test-suite/csharp/preproc_constants_runme.cs index 6af8f20a4..d79e2c302 100644 --- a/Examples/test-suite/csharp/preproc_constants_runme.cs +++ b/Examples/test-suite/csharp/preproc_constants_runme.cs @@ -50,6 +50,8 @@ public class runme { assert( typeof(int) == preproc_constants.EXPR_MINUS.GetType() ); assert( typeof(int) == preproc_constants.EXPR_LSHIFT.GetType() ); assert( typeof(int) == preproc_constants.EXPR_RSHIFT.GetType() ); + assert( typeof(bool) == preproc_constants.EXPR_LT.GetType() ); + assert( typeof(bool) == preproc_constants.EXPR_GT.GetType() ); assert( typeof(bool) == preproc_constants.EXPR_LTE.GetType() ); assert( typeof(bool) == preproc_constants.EXPR_GTE.GetType() ); assert( typeof(bool) == preproc_constants.EXPR_INEQUALITY.GetType() ); diff --git a/Examples/test-suite/d/preproc_constants_c_runme.1.d b/Examples/test-suite/d/preproc_constants_c_runme.1.d index a6c2f3d10..f98f37b5f 100644 --- a/Examples/test-suite/d/preproc_constants_c_runme.1.d +++ b/Examples/test-suite/d/preproc_constants_c_runme.1.d @@ -51,6 +51,8 @@ void main() { static assert(is(int == typeof(EXPR_MINUS()))); static assert(is(int == typeof(EXPR_LSHIFT()))); static assert(is(int == typeof(EXPR_RSHIFT()))); + static assert(is(int == typeof(EXPR_LT()))); + static assert(is(int == typeof(EXPR_GT()))); static assert(is(int == typeof(EXPR_LTE()))); static assert(is(int == typeof(EXPR_GTE()))); static assert(is(int == typeof(EXPR_INEQUALITY()))); diff --git a/Examples/test-suite/d/preproc_constants_c_runme.2.d b/Examples/test-suite/d/preproc_constants_c_runme.2.d index 786cb48cc..caf3029b2 100644 --- a/Examples/test-suite/d/preproc_constants_c_runme.2.d +++ b/Examples/test-suite/d/preproc_constants_c_runme.2.d @@ -51,6 +51,8 @@ void main() { static assert(is(int == typeof(EXPR_MINUS()))); static assert(is(int == typeof(EXPR_LSHIFT()))); static assert(is(int == typeof(EXPR_RSHIFT()))); + static assert(is(int == typeof(EXPR_LT()))); + static assert(is(int == typeof(EXPR_GT()))); static assert(is(int == typeof(EXPR_LTE()))); static assert(is(int == typeof(EXPR_GTE()))); static assert(is(int == typeof(EXPR_INEQUALITY()))); diff --git a/Examples/test-suite/d/preproc_constants_runme.1.d b/Examples/test-suite/d/preproc_constants_runme.1.d index 85fa918e4..95f3c4757 100644 --- a/Examples/test-suite/d/preproc_constants_runme.1.d +++ b/Examples/test-suite/d/preproc_constants_runme.1.d @@ -50,6 +50,8 @@ void main() { static assert(is(int == typeof(EXPR_MINUS()))); static assert(is(int == typeof(EXPR_LSHIFT()))); static assert(is(int == typeof(EXPR_RSHIFT()))); + static assert(is(int == typeof(EXPR_LT()))); + static assert(is(int == typeof(EXPR_GT()))); static assert(is(bool == typeof(EXPR_LTE()))); static assert(is(bool == typeof(EXPR_GTE()))); static assert(is(bool == typeof(EXPR_INEQUALITY()))); diff --git a/Examples/test-suite/d/preproc_constants_runme.2.d b/Examples/test-suite/d/preproc_constants_runme.2.d index c81e53160..416384a11 100644 --- a/Examples/test-suite/d/preproc_constants_runme.2.d +++ b/Examples/test-suite/d/preproc_constants_runme.2.d @@ -50,6 +50,8 @@ void main() { static assert(is(int == typeof(EXPR_MINUS()))); static assert(is(int == typeof(EXPR_LSHIFT()))); static assert(is(int == typeof(EXPR_RSHIFT()))); + static assert(is(bool == typeof(EXPR_LT()))); + static assert(is(bool == typeof(EXPR_GT()))); static assert(is(bool == typeof(EXPR_LTE()))); static assert(is(bool == typeof(EXPR_GTE()))); static assert(is(bool == typeof(EXPR_INEQUALITY()))); diff --git a/Examples/test-suite/php/preproc_constants_c_runme.php b/Examples/test-suite/php/preproc_constants_c_runme.php index 411f7d766..688da52f6 100644 --- a/Examples/test-suite/php/preproc_constants_c_runme.php +++ b/Examples/test-suite/php/preproc_constants_c_runme.php @@ -51,6 +51,8 @@ check::equal(gettype(preproc_constants_c::EXPR_PLUS), "integer", "preproc_consta check::equal(gettype(preproc_constants_c::EXPR_MINUS), "integer", "preproc_constants.EXPR_MINUS has unexpected type"); check::equal(gettype(preproc_constants_c::EXPR_LSHIFT), "integer", "preproc_constants.EXPR_LSHIFT has unexpected type"); check::equal(gettype(preproc_constants_c::EXPR_RSHIFT), "integer", "preproc_constants.EXPR_RSHIFT has unexpected type"); +check::equal(gettype(preproc_constants_c::EXPR_LT), "integer", "preproc_constants.EXPR_LTE has unexpected type"); +check::equal(gettype(preproc_constants_c::EXPR_GT), "integer", "preproc_constants.EXPR_GTE has unexpected type"); check::equal(gettype(preproc_constants_c::EXPR_LTE), "integer", "preproc_constants.EXPR_LTE has unexpected type"); check::equal(gettype(preproc_constants_c::EXPR_GTE), "integer", "preproc_constants.EXPR_GTE has unexpected type"); check::equal(gettype(preproc_constants_c::EXPR_INEQUALITY), "integer", "preproc_constants.EXPR_INEQUALITY has unexpected type"); diff --git a/Examples/test-suite/preproc_constants.i b/Examples/test-suite/preproc_constants.i index 3050baa06..f0c5e9361 100644 --- a/Examples/test-suite/preproc_constants.i +++ b/Examples/test-suite/preproc_constants.i @@ -72,10 +72,8 @@ #define EXPR_LSHIFT 0xFF << 2 #define EXPR_RSHIFT 0xFF >> 2 -/* FIXME -#define EXPR_LT 0xFF < 255 -#define EXPR_GT 0xFF > 255 -*/ +#define EXPR_LT (0xFF < 255) +#define EXPR_GT (0xFF > 255) #define EXPR_LTE 0xFF <= 255 #define EXPR_GTE 0xFF >= 255 #define EXPR_INEQUALITY 0xFF != 255 diff --git a/RELEASENOTES b/RELEASENOTES index 3a596dd46..1fedffc8a 100644 --- a/RELEASENOTES +++ b/RELEASENOTES @@ -11,6 +11,8 @@ SWIG-4.1.0 summary: - Add PHP 8 support. - PHP wrapping is now done entirely via PHP's C API - no more .php wrapper. - Perl 5.8.0 is now the oldest version SWIG supports. +- Common cases of `<` and `>` comparisons in constant expressions are now + supported. - GitHub Actions is now used instead of Travis CI for continuous integration. SWIG-4.0.2 summary: diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index 0796362bc..562c51269 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -1675,7 +1675,7 @@ static String *add_qualifier_to_declarator(SwigType *type, SwigType *qualifier) %type type rawtype type_right anon_bitfield_type decltype ; %type base_list inherit raw_inherit; %type definetype def_args etype default_delete deleted_definition explicit_default; -%type expr exprnum exprcompound valexpr exprmem; +%type expr exprnum exprsimple exprcompound valexpr exprmem; %type ename ; %type less_valparms_greater; %type type_qualifier; @@ -6533,7 +6533,8 @@ exprmem : ID ARROW ID { } ; -valexpr : exprnum { +/* Non-compound expression */ +exprsimple : exprnum { $$ = $1; } | exprmem { @@ -6553,7 +6554,6 @@ valexpr : exprnum { $$.val = NewStringf("sizeof...(%s)",SwigType_str($6,0)); $$.type = T_ULONG; } - | exprcompound { $$ = $1; } | wstring { $$.val = $1; $$.rawval = NewStringf("L\"%s\"", $$.val); @@ -6588,6 +6588,11 @@ valexpr : exprnum { $$.final = 0; } + ; + +valexpr : exprsimple { $$ = $1; } + | exprcompound { $$ = $1; } + /* grouping */ | LPAREN expr RPAREN %prec CAST { $$.val = NewStringf("(%s)",$2.val); @@ -6666,7 +6671,7 @@ valexpr : exprnum { $$ = $2; $$.val = NewStringf("*%s",$2.val); } - ; + ; exprnum : NUM_INT { $$ = $1; } | NUM_FLOAT { $$ = $1; } @@ -6734,16 +6739,24 @@ exprcompound : expr PLUS expr { $$.val = NewStringf("%s!=%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3)); $$.type = cparse_cplusplus ? T_BOOL : T_INT; } -/* Sadly this causes 2 reduce-reduce conflicts with templates. FIXME resolve these. - | expr GREATERTHAN expr { - $$.val = NewStringf("%s > %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3)); + /* 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 GREATERTHAN expr RPAREN { + $$.val = NewStringf("%s > %s", COMPOUND_EXPR_VAL($2), COMPOUND_EXPR_VAL($4)); $$.type = cparse_cplusplus ? T_BOOL : T_INT; } - | expr LESSTHAN expr { - $$.val = NewStringf("%s < %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3)); + + /* 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 LESSTHAN expr RPAREN { + $$.val = NewStringf("%s < %s", COMPOUND_EXPR_VAL($2), COMPOUND_EXPR_VAL($4)); $$.type = cparse_cplusplus ? T_BOOL : T_INT; } -*/ | expr GREATERTHANOREQUALTO expr { $$.val = NewStringf("%s >= %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3)); $$.type = cparse_cplusplus ? T_BOOL : T_INT;