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;