Improve type deduction

Create a new T_UNKNOWN type code and use this for the cases where
we previously abused T_INT.  This means we can now reliably deduce
`int` when we see T_INT.

Fix the deduced result types of unary plus and unary minus which weren't
getting integer promotion applied to them.

Fix the deduced result type of the C++ logical not operator which was
`int` but should be `bool`.
This commit is contained in:
Olly Betts 2023-11-18 18:15:06 +13:00
parent 940b41f40a
commit 997616e652
8 changed files with 45 additions and 17 deletions

View File

@ -548,8 +548,6 @@ Version 4.2.0 (in progress)
2023-05-25: olly
C++11 `auto` variables and `decltype()` can now deduce the
type of some expressions which involve literals of built-in types.
Currently int is not supported due to the parser misusing T_INT
for situations where the type isn't actually known.
2023-05-25: olly
#1125 Support parsing C++11 auto variables. This uses the

View File

@ -5,6 +5,9 @@
static auto t = true;
static constexpr auto f = false;
static auto zero = 0;
static constexpr auto one = 1;
static auto la = 1.0L;
static auto da = 1.0;
static auto fa = 1.0f;

View File

@ -35,6 +35,15 @@
DECLARE(a, false);
DECLARE(b, true);
// SWIG < 4.2.0 failed to perform type promotion for the result of unary
// plus and unary minus, so these would end up wrapped as bool and char.
decltype(+true) should_be_int;
decltype(-'x') should_be_int2;
// SWIG < 4.2.0 incorrectly used int for the result of logical not in C++
// so this would end up wrapped as int.
decltype(!0) should_be_bool;
auto get_number_sum(decltype(i+j) a) -> decltype(i+j) {
return i+j;
}

View File

@ -5,16 +5,28 @@ require "tests.php";
// No new functions
check::functions(array());
check::classes(array('cpp11_auto_variable'));
check::globals(array('f', 't', 'la', 'da', 'fa', 'lc', 'dc', 'fc', 'pi_approx', 'Bar', 'Bar2', 'Foo', 'Foo2'));
check::globals(array('f', 't', 'zero', 'one', 'la', 'da', 'fa', 'lc', 'dc', 'fc', 'pi_approx', 'Bar', 'Bar2', 'Foo', 'Foo2'));
check::equal(f_get(), false);
check::equal(gettype(f_get()), "boolean");
check::equal(t_get(), true);
check::equal(gettype(t_get()), "boolean");
t_set(false);
check::equal(t_get(), false);
check::equal(function_exists('f_set'), false, "f should be constant but f_set() exists");
check::equal(zero_get(), 0);
check::equal(gettype(zero_get()), "integer");
check::equal(one_get(), 1);
check::equal(gettype(one_get()), "integer");
zero_set(42);
check::equal(zero_get(), 42);
check::equal(function_exists('one_set'), false, "one should be constant but one_set() exists");
check::equal(fa_get(), 1.0);
check::equal(da_get(), 1.0);
// PHP doesn't have a native "long double" type, so SWIG/PHP doesn't have

View File

@ -31,3 +31,7 @@ check::equal($b->b, true);
check::equal($b->negate(true), false);
check::equal($b->negate(false), true);
check::equal(gettype($b->should_be_int), "integer");
check::equal(gettype($b->should_be_int2), "integer");
check::equal(gettype($b->should_be_bool), "boolean");

View File

@ -1780,12 +1780,8 @@ static SwigType *deduce_type(const struct Define *dtype) {
Node *n = Swig_symbol_clookup(dtype->val, 0);
if (n) {
return Getattr(n, "type");
} else if (dtype->type != T_AUTO && dtype->type != T_INT) {
/* Try to deduce the type from the T_* type code.
*
* Sadly we can't trust T_INT because several places in the parser set
* .type = T_INT when it may not be (or even definitely isn't).
*/
} else if (dtype->type != T_AUTO && dtype->type != T_UNKNOWN) {
/* Try to deduce the type from the T_* type code. */
return NewSwigType(dtype->type);
} else {
return NULL;
@ -5304,7 +5300,7 @@ def_args : EQUAL definetype {
if (skip_balanced('{','}') < 0) Exit(EXIT_FAILURE);
$$.val = NewString(scanner_ccode);
$$.rawval = 0;
$$.type = T_INT; /* This may be a lie. */
$$.type = T_UNKNOWN;
$$.bitfield = 0;
$$.throws = 0;
$$.throwf = 0;
@ -5324,7 +5320,7 @@ def_args : EQUAL definetype {
| %empty {
$$.val = 0;
$$.rawval = 0;
$$.type = T_INT; /* This is a lie. */
$$.type = T_UNKNOWN;
$$.bitfield = 0;
$$.throws = 0;
$$.throwf = 0;
@ -6549,7 +6545,8 @@ etype : expr {
($$.type != T_LONGLONG) && ($$.type != T_ULONGLONG) &&
($$.type != T_SHORT) && ($$.type != T_USHORT) &&
($$.type != T_SCHAR) && ($$.type != T_UCHAR) &&
($$.type != T_CHAR) && ($$.type != T_BOOL)) {
($$.type != T_CHAR) && ($$.type != T_BOOL) &&
($$.type != T_UNKNOWN)) {
Swig_error(cparse_file,cparse_line,"Type error. Expecting an integral type\n");
}
}
@ -6561,7 +6558,7 @@ expr : valexpr
| type {
Node *n;
$$.val = $1;
$$.type = T_INT; /* This may be a lie. */
$$.type = T_UNKNOWN;
/* Check if value is in scope */
n = Swig_symbol_clookup($1,0);
if (n) {
@ -6878,11 +6875,11 @@ exprcompound : expr PLUS expr {
}
| MINUS expr %prec UMINUS {
$$.val = NewStringf("-%s",$2.val);
$$.type = $2.type;
$$.type = promote_type($2.type);
}
| PLUS expr %prec UMINUS {
$$.val = NewStringf("+%s",$2.val);
$$.type = $2.type;
$$.type = promote_type($2.type);
}
| NOT expr {
$$.val = NewStringf("~%s",$2.val);
@ -6890,7 +6887,7 @@ exprcompound : expr PLUS expr {
}
| LNOT expr {
$$.val = NewStringf("!%s",COMPOUND_EXPR_VAL($2));
$$.type = T_INT;
$$.type = cparse_cplusplus ? T_BOOL : T_INT;
}
| type LPAREN {
String *qty;
@ -6903,7 +6900,7 @@ exprcompound : expr PLUS expr {
}
$$.val = NewStringf("%s%s",qty,scanner_ccode);
Clear(scanner_ccode);
$$.type = T_INT;
$$.type = T_UNKNOWN;
Delete(qty);
}
;

View File

@ -109,6 +109,10 @@ SwigType *NewSwigType(int t) {
switch (t) {
case T_BOOL:
return NewString("bool");
case T_UNKNOWN:
/* Handle like T_INT since we used to just use T_INT where we now use
* T_UNKNOWN.
*/
case T_INT:
return NewString("int");
case T_UINT:

View File

@ -95,6 +95,7 @@ extern "C" {
#define T_VARARGS 39
#define T_RVALUE_REFERENCE 40
#define T_WSTRING 41
#define T_UNKNOWN 42
/* --- File interface --- */