From cb887ed2d477a2ebc2b14aa5d5ec063e5ce9309f Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Tue, 3 Sep 2019 16:48:24 -0600 Subject: [PATCH 1/2] Add support for the <=> operator (C++20) --- Lib/swigwarnings.swg | 1 + Source/CParse/cscanner.c | 2 ++ Source/CParse/parser.y | 8 ++++++-- Source/Include/swigwarn.h | 1 + Source/Swig/scanner.c | 14 ++++++++++++-- Source/Swig/swigscan.h | 1 + 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Lib/swigwarnings.swg b/Lib/swigwarnings.swg index 3f9ddcb58..63ae4c65a 100644 --- a/Lib/swigwarnings.swg +++ b/Lib/swigwarnings.swg @@ -108,6 +108,7 @@ %define SWIGWARN_IGNORE_OPERATOR_NEWARR_MSG "394:operator new[] ignored" %enddef %define SWIGWARN_IGNORE_OPERATOR_DELARR_MSG "395:operator delete[] ignored" %enddef %define SWIGWARN_IGNORE_OPERATOR_REF_MSG "396:operator*() ignored" %enddef +%define SWIGWARN_IGNORE_OPERATOR_LTEQUALGT_MSG "397:operator<=> ignored" %enddef #define %ignoreoperator(Oper) %ignorewarn(SWIGWARN_IGNORE_OPERATOR_##Oper##_MSG) diff --git a/Source/CParse/cscanner.c b/Source/CParse/cscanner.c index 558a9c6cf..db0e6df27 100644 --- a/Source/CParse/cscanner.c +++ b/Source/CParse/cscanner.c @@ -331,6 +331,8 @@ static int yylook(void) { return COLON; case SWIG_TOKEN_DCOLONSTAR: return DSTAR; + case SWIG_TOKEN_LTEQUALGT: + return LESSEQUALGREATER; case SWIG_TOKEN_DCOLON: { diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index 1a5d4a201..e2d37dae0 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -1626,7 +1626,7 @@ static String *add_qualifier_to_declarator(SwigType *type, SwigType *qualifier) %token TYPEMAP EXCEPT ECHO APPLY CLEAR SWIGTEMPLATE FRAGMENT %token WARN %token LESSTHAN GREATERTHAN DELETE_KW DEFAULT -%token LESSTHANOREQUALTO GREATERTHANOREQUALTO EQUALTO NOTEQUALTO +%token LESSTHANOREQUALTO GREATERTHANOREQUALTO EQUALTO NOTEQUALTO LESSEQUALGREATER %token ARROW %token QUESTIONMARK %token TYPES PARMS @@ -1647,7 +1647,7 @@ static String *add_qualifier_to_declarator(SwigType *type, SwigType *qualifier) %left XOR %left AND %left EQUALTO NOTEQUALTO -%left GREATERTHAN LESSTHAN GREATERTHANOREQUALTO LESSTHANOREQUALTO +%left GREATERTHAN LESSTHAN GREATERTHANOREQUALTO LESSTHANOREQUALTO LESSEQUALGREATER %left LSHIFT RSHIFT %left PLUS MINUS %left STAR SLASH MODULO @@ -6844,6 +6844,10 @@ exprcompound : expr PLUS expr { $$.val = NewStringf("%s <= %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3)); $$.type = cparse_cplusplus ? T_BOOL : T_INT; } + | expr LESSEQUALGREATER expr { + $$.val = NewStringf("%s <=> %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3)); + $$.type = T_BOOL; + } | expr QUESTIONMARK expr COLON expr %prec QUESTIONMARK { $$.val = NewStringf("%s?%s:%s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3), COMPOUND_EXPR_VAL($5)); /* This may not be exactly right, but is probably good enough diff --git a/Source/Include/swigwarn.h b/Source/Include/swigwarn.h index f37cb0ef2..89df97a88 100644 --- a/Source/Include/swigwarn.h +++ b/Source/Include/swigwarn.h @@ -147,6 +147,7 @@ #define WARN_IGNORE_OPERATOR_NEWARR 394 /* new [] */ #define WARN_IGNORE_OPERATOR_DELARR 395 /* delete [] */ #define WARN_IGNORE_OPERATOR_REF 396 /* operator *() */ +#define WARN_IGNORE_OPERATOR_LTEQUALGT 397 /* <=> */ /* please leave 350-399 free for WARN_IGNORE_OPERATOR_* */ diff --git a/Source/Swig/scanner.c b/Source/Swig/scanner.c index b03a07a5b..961da8343 100644 --- a/Source/Swig/scanner.c +++ b/Source/Swig/scanner.c @@ -893,8 +893,18 @@ static int look(Scanner *s) { } if (c == '<') state = 240; - else if (c == '=') - return SWIG_TOKEN_LTEQUAL; + else if (c == '=') { + if ((c = nextchar(s)) == 0) { + brackets_increment(s); + return SWIG_TOKEN_LTEQUAL; + } else if (c == '>') { /* Spaceship operator */ + return SWIG_TOKEN_LTEQUALGT; + } else { + retract(s, 1); + brackets_increment(s); + return SWIG_TOKEN_LTEQUAL; + } + } else { retract(s, 1); brackets_increment(s); diff --git a/Source/Swig/swigscan.h b/Source/Swig/swigscan.h index 526413a14..0476e8587 100644 --- a/Source/Swig/swigscan.h +++ b/Source/Swig/swigscan.h @@ -116,3 +116,4 @@ extern void Scanner_locator(Scanner *, String *loc); #define SWIG_TOKEN_MODEQUAL 134 /* %= */ #define SWIG_TOKEN_ARROW 135 /* -> */ #define SWIG_TOKEN_ARROWSTAR 136 /* ->* */ +#define SWIG_TOKEN_LTEQUALGT 137 /* <=> */ From 02b4bd8eca73c6de0bb56f866f19fc30c63b7ba9 Mon Sep 17 00:00:00 2001 From: Olly Betts Date: Wed, 9 Mar 2022 10:08:17 +1300 Subject: [PATCH 2/2] Fix spaceship operator bugs, document, add tests Remove some erroneously added brackets_increment() calls. Reject <=> in preprocessor expressions with a clear error message (it seems it isn't supported here - clang and gcc don't at least). The type returned by `<=>` is not `bool`. We pretend it's `int` for now, which should work for how it's likely to be used in constant expressions. Fixes #1622 --- Doc/Devel/scanner.html | 1 + Doc/Manual/CPlusPlus20.html | 15 ++++++++++ Examples/test-suite/common.mk | 1 + .../test-suite/cpp20_spaceship_operator.i | 28 +++++++++++++++++++ .../test-suite/errors/pp_expressions_bad.i | 4 +++ .../errors/pp_expressions_bad.stderr | 2 ++ .../php/cpp20_spaceship_operator_runme.php | 23 +++++++++++++++ Source/CParse/parser.y | 12 ++++++-- Source/Preprocessor/expr.c | 7 +++++ Source/Swig/scanner.c | 5 +--- 10 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 Examples/test-suite/cpp20_spaceship_operator.i create mode 100644 Examples/test-suite/php/cpp20_spaceship_operator_runme.php diff --git a/Doc/Devel/scanner.html b/Doc/Devel/scanner.html index 65ef1d8e9..d620c3d98 100644 --- a/Doc/Devel/scanner.html +++ b/Doc/Devel/scanner.html @@ -204,6 +204,7 @@ SWIG_TOKEN_LESSTHAN < SWIG_TOKEN_GREATERTHAN > SWIG_TOKEN_LTEQUAL <= SWIG_TOKEN_GTEQUAL >= +SWIG_TOKEN_LTEQUALGT <=> SWIG_TOKEN_NOT ~ SWIG_TOKEN_LNOT ! SWIG_TOKEN_LBRACKET [ diff --git a/Doc/Manual/CPlusPlus20.html b/Doc/Manual/CPlusPlus20.html index 0a8b0027f..8db84bfd5 100644 --- a/Doc/Manual/CPlusPlus20.html +++ b/Doc/Manual/CPlusPlus20.html @@ -34,6 +34,21 @@ Work has only just begun on adding C++20 support.

10.2 Core language changes

+

10.2.1 Spaceship operator

+ +

+SWIG supports the spaceship operator <=> in constant +expressions. To simplifying handling the return value is currently +treated as an integer rather than std::strong_ordering, etc. +In practice we think that should do the right thing in most cases. +

+ +

+SWIG also recognises operator<=> which can be wrapped +if renamed. There's not currently any default renaming for the operator +or attempts to automatically map it to a three-way comparison operator +in any target languages that have one. +

10.3 Standard library changes

diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index c1b339c54..838ea5d2a 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -649,6 +649,7 @@ CPP17_TEST_BROKEN = \ # C++20 test cases. CPP20_TEST_CASES += \ + cpp20_spaceship_operator \ # Broken C++20 test cases. CPP20_TEST_BROKEN = \ diff --git a/Examples/test-suite/cpp20_spaceship_operator.i b/Examples/test-suite/cpp20_spaceship_operator.i new file mode 100644 index 000000000..ddece13a0 --- /dev/null +++ b/Examples/test-suite/cpp20_spaceship_operator.i @@ -0,0 +1,28 @@ +%module cpp20_spaceship_operator + +%rename(spaceship) operator<=>; + +%inline %{ +#include + +int v = (-1 <=> 1 > 0) ? 7 : 42; + +// We use !(a >= b) here due to limited support for (a < b) in SWIG's parser. +#define ALIEN !(0 <=> 1 >= 0) + +const int SPACE = 3 <=> 3 == 0; + +struct A { + int v; + + explicit A(int v_) : v(v_) { } +}; + +int operator<=>(const A& a, const A& b) { + return a.v - b.v; +} + +int f(int v = (-1 <=> 1 > 0) ? 7 : 42) { return v; } +%} + +%constant int COMET = (4 <=> 2 > 0); diff --git a/Examples/test-suite/errors/pp_expressions_bad.i b/Examples/test-suite/errors/pp_expressions_bad.i index 445ed7949..3fa49ba4a 100644 --- a/Examples/test-suite/errors/pp_expressions_bad.i +++ b/Examples/test-suite/errors/pp_expressions_bad.i @@ -66,3 +66,7 @@ /* Unary + was a no-op and so this didn't give an error in SWIG < 4.1.0. */ #if "1" == +"1" #endif + +/* Spaceship operator doesn't seem to be allowed in preprocessor expressions. */ +#if (4 <=> 2) < 0 +#endif diff --git a/Examples/test-suite/errors/pp_expressions_bad.stderr b/Examples/test-suite/errors/pp_expressions_bad.stderr index 37f846c03..12eb42e04 100644 --- a/Examples/test-suite/errors/pp_expressions_bad.stderr +++ b/Examples/test-suite/errors/pp_expressions_bad.stderr @@ -31,3 +31,5 @@ pp_expressions_bad.i:64: Warning 202: Could not evaluate expression '"1" == ~"1" pp_expressions_bad.i:64: Warning 202: Syntax error: attempt to apply unary operator to string pp_expressions_bad.i:67: Warning 202: Could not evaluate expression '"1" == +"1"' pp_expressions_bad.i:67: Warning 202: Syntax error: attempt to apply unary operator to string +pp_expressions_bad.i:71: Warning 202: Could not evaluate expression '(4 <=> 2) < 0' +pp_expressions_bad.i:71: Warning 202: Spaceship operator (<=>) not allowed in preprocessor expression diff --git a/Examples/test-suite/php/cpp20_spaceship_operator_runme.php b/Examples/test-suite/php/cpp20_spaceship_operator_runme.php new file mode 100644 index 000000000..00484f742 --- /dev/null +++ b/Examples/test-suite/php/cpp20_spaceship_operator_runme.php @@ -0,0 +1,23 @@ + 0, true); + +check::equal(f(), 42); + +check::done(); diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index e2d37dae0..be5159a62 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -1647,7 +1647,8 @@ static String *add_qualifier_to_declarator(SwigType *type, SwigType *qualifier) %left XOR %left AND %left EQUALTO NOTEQUALTO -%left GREATERTHAN LESSTHAN GREATERTHANOREQUALTO LESSTHANOREQUALTO LESSEQUALGREATER +%left GREATERTHAN LESSTHAN GREATERTHANOREQUALTO LESSTHANOREQUALTO +%left LESSEQUALGREATER %left LSHIFT RSHIFT %left PLUS MINUS %left STAR SLASH MODULO @@ -6844,9 +6845,14 @@ exprcompound : expr PLUS expr { $$.val = NewStringf("%s <= %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3)); $$.type = cparse_cplusplus ? T_BOOL : T_INT; } - | expr LESSEQUALGREATER expr { + | expr LESSEQUALGREATER expr { $$.val = NewStringf("%s <=> %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3)); - $$.type = T_BOOL; + // Really `<=>` returns one of `std::strong_ordering`, + // `std::partial_ordering` or `std::weak_ordering`, but we + // fake it by treating the return value as `int`. The main + // thing to do with the return value in this context is to + // compare it with 0, for which `int` does the job. + $$.type = T_INT; } | expr QUESTIONMARK expr COLON expr %prec QUESTIONMARK { $$.val = NewStringf("%s?%s:%s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3), COMPOUND_EXPR_VAL($5)); diff --git a/Source/Preprocessor/expr.c b/Source/Preprocessor/expr.c index b03060939..557e0e752 100644 --- a/Source/Preprocessor/expr.c +++ b/Source/Preprocessor/expr.c @@ -445,6 +445,8 @@ int Preprocessor_expr(DOH *s, int *error) { stack[sp - 1].svalue = stack[sp].svalue; sp--; break; + case SWIG_TOKEN_LTEQUALGT: + goto spaceship_not_allowed; default: goto syntax_error_expected_operator; break; @@ -476,6 +478,11 @@ extra_rparen: errmsg = "Extra \')\'"; *error = 1; return 0; + +spaceship_not_allowed: + errmsg = "Spaceship operator (<=>) not allowed in preprocessor expression"; + *error = 1; + return 0; } /* ----------------------------------------------------------------------------- diff --git a/Source/Swig/scanner.c b/Source/Swig/scanner.c index 961da8343..4ac3e6c3c 100644 --- a/Source/Swig/scanner.c +++ b/Source/Swig/scanner.c @@ -895,17 +895,14 @@ static int look(Scanner *s) { state = 240; else if (c == '=') { if ((c = nextchar(s)) == 0) { - brackets_increment(s); return SWIG_TOKEN_LTEQUAL; } else if (c == '>') { /* Spaceship operator */ return SWIG_TOKEN_LTEQUALGT; } else { retract(s, 1); - brackets_increment(s); return SWIG_TOKEN_LTEQUAL; } - } - else { + } else { retract(s, 1); brackets_increment(s); return SWIG_TOKEN_LESSTHAN;