Support deducing type of some expressions

This change only supports expressions involving literals of built-in
types, and `int` expressions aren't supported (due to the parser
currently setting .type=T_INT when in situations where that isn't
or may not be the correct type).
diff --git a/CHANGES.current b/CHANGES.current
index 6ff0868..c770089 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -8,6 +8,12 @@
 ===========================
 
 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
 	    existing type deduction code from decltype so has the same
 	    limitations, and such variables will only actually be wrapped
diff --git a/Doc/Devel/scanner.html b/Doc/Devel/scanner.html
index 94807ff..cda102f 100644
--- a/Doc/Devel/scanner.html
+++ b/Doc/Devel/scanner.html
@@ -242,6 +242,7 @@
 SWIG_TOKEN_ID                identifier 
 SWIG_TOKEN_FLOAT             Floating point with F suffix (e.g., 3.1415F)
 SWIG_TOKEN_DOUBLE            Floating point (e.g., 3.1415 )
+SWIG_TOKEN_LONGDOUBLE        Floating point with L suffix (e.g., 3.1415L)
 SWIG_TOKEN_INT               Integer (e.g., 314)
 SWIG_TOKEN_UINT              Unsigned integer (e.g., 314U)
 SWIG_TOKEN_LONG              Long integer (e.g., 314L) 
diff --git a/Doc/Manual/CPlusPlus11.html b/Doc/Manual/CPlusPlus11.html
index 06cc2fa..9aceb8d 100644
--- a/Doc/Manual/CPlusPlus11.html
+++ b/Doc/Manual/CPlusPlus11.html
@@ -721,8 +721,9 @@
 
 
 <p><tt>decltype()</tt> is supported with limitations.  SWIG can parse
-all uses, but can only deduce the type for single variables, <tt>true</tt> and
-<tt>false</tt>. For example, the following code will work:</p>
+all uses, but can only deduce the type for single variables, and for
+some expressions involving only literal values of built-in C++ types.
+For example, the following code will work:</p>
 <div class="code"><pre>
 int i;
 decltype(i) j;
diff --git a/Examples/test-suite/cpp11_auto_variable.i b/Examples/test-suite/cpp11_auto_variable.i
index da05f57..67988b9 100644
--- a/Examples/test-suite/cpp11_auto_variable.i
+++ b/Examples/test-suite/cpp11_auto_variable.i
@@ -5,6 +5,21 @@
 static auto t = true;
 static constexpr auto f = false;
 
+static auto la = 1.0L;
+static auto da = 1.0;
+static auto fa = 1.0f;
+static constexpr auto lc = 1.0L;
+static constexpr auto dc = 1.0;
+static constexpr auto fc = 1.0f;
+
+static constexpr auto pi_approx = 355. / 133;
+
+static constexpr auto Bar = "foo";
+static constexpr auto Foo = "bar";
+
+static constexpr auto Bar2 = Bar;
+static constexpr auto Foo2 = Foo;
+
 %}
 
 // SWIG currently can't deduce the type for examples below:
@@ -12,12 +27,6 @@
 
 %inline %{
 
-static auto Bar = "foo";
-static constexpr auto Foo = "bar";
-
-static auto Bar2 = Bar;
-static constexpr auto Foo2 = Foo;
-
 static auto Bar3 = f ? Bar : Bar2;
 static constexpr auto Foo3 = f ? Foo : Foo2;
 
diff --git a/Examples/test-suite/cpp11_template_parameters_decltype.i b/Examples/test-suite/cpp11_template_parameters_decltype.i
index 257d1ec..f61e489 100644
--- a/Examples/test-suite/cpp11_template_parameters_decltype.i
+++ b/Examples/test-suite/cpp11_template_parameters_decltype.i
@@ -4,6 +4,15 @@
 %include <std_vector.i>
 %include <std_pair.i>
 
+%inline %{
+// Github issue #1589
+template <decltype(true) X = true>
+void A() { }
+%}
+
+// %template(A) A<>; // not working
+%template(A) A<true>; // workaround
+
 #pragma SWIG nowarn=SWIGWARN_CPP11_DECLTYPE
 
 // Non-template expression equivalent to template expression further down:
@@ -34,13 +43,3 @@
 
 // %template(Json) Json::Json<Converter>; // not working
 %template(Json) Json::Json<Converter, std::string>; // workaround
-
-
-%inline %{
-// Github issue #1589
-template <decltype(true) X = true>
-void A() { }
-%}
-
-// %template(A) A<>; // not working
-%template(A) A<true>; // workaround
diff --git a/Examples/test-suite/php/cpp11_auto_variable_runme.php b/Examples/test-suite/php/cpp11_auto_variable_runme.php
index c8ddc56..5e831a8 100644
--- a/Examples/test-suite/php/cpp11_auto_variable_runme.php
+++ b/Examples/test-suite/php/cpp11_auto_variable_runme.php
@@ -5,7 +5,7 @@
 // No new functions
 check::functions(array());
 check::classes(array('cpp11_auto_variable'));
-check::globals(array('f', 't'));
+check::globals(array('f', 't', 'la', 'da', 'fa', 'lc', 'dc', 'fc', 'pi_approx', 'Bar', 'Bar2', 'Foo', 'Foo2'));
 
 check::equal(f_get(), false);
 check::equal(t_get(), true);
@@ -14,3 +14,15 @@
 check::equal(t_get(), false);
 
 check::equal(function_exists('f_set'), false, "f should be constant but f_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
+// typemaps for it and so it should get wrapped as an opaque type.
+check::str_contains(la_get(), "SWIGPointer(");
+
+check::equal(fc_get(), 1.0);
+check::equal(dc_get(), 1.0);
+// PHP doesn't have a native "long double" type, so SWIG/PHP doesn't have
+// typemaps for it and so it should get wrapped as an opaque type.
+check::str_contains(lc_get(), "SWIGPointer(");
diff --git a/Source/CParse/cscanner.c b/Source/CParse/cscanner.c
index 3395131..3d17db0 100644
--- a/Source/CParse/cscanner.c
+++ b/Source/CParse/cscanner.c
@@ -421,9 +421,14 @@
       return NUM_ULONGLONG;
       
     case SWIG_TOKEN_DOUBLE:
+      return NUM_DOUBLE;
+
     case SWIG_TOKEN_FLOAT:
       return NUM_FLOAT;
       
+    case SWIG_TOKEN_LONGDOUBLE:
+      return NUM_LONGDOUBLE;
+
     case SWIG_TOKEN_BOOL:
       return NUM_BOOL;
       
@@ -622,29 +627,35 @@
   switch (l) {
 
   case NUM_INT:
+    yylval.dtype.type = T_INT;
+    goto num_common;
+  case NUM_DOUBLE:
+    yylval.dtype.type = T_DOUBLE;
+    goto num_common;
   case NUM_FLOAT:
+    yylval.dtype.type = T_FLOAT;
+    goto num_common;
+  case NUM_LONGDOUBLE:
+    yylval.dtype.type = T_LONGDOUBLE;
+    goto num_common;
   case NUM_ULONG:
+    yylval.dtype.type = T_ULONG;
+    goto num_common;
   case NUM_LONG:
+    yylval.dtype.type = T_LONG;
+    goto num_common;
   case NUM_UNSIGNED:
+    yylval.dtype.type = T_UINT;
+    goto num_common;
   case NUM_LONGLONG:
+    yylval.dtype.type = T_LONGLONG;
+    goto num_common;
   case NUM_ULONGLONG:
+    yylval.dtype.type = T_ULONGLONG;
+    goto num_common;
   case NUM_BOOL:
-    if (l == NUM_INT)
-      yylval.dtype.type = T_INT;
-    if (l == NUM_FLOAT)
-      yylval.dtype.type = T_DOUBLE;
-    if (l == NUM_ULONG)
-      yylval.dtype.type = T_ULONG;
-    if (l == NUM_LONG)
-      yylval.dtype.type = T_LONG;
-    if (l == NUM_UNSIGNED)
-      yylval.dtype.type = T_UINT;
-    if (l == NUM_LONGLONG)
-      yylval.dtype.type = T_LONGLONG;
-    if (l == NUM_ULONGLONG)
-      yylval.dtype.type = T_ULONGLONG;
-    if (l == NUM_BOOL)
-      yylval.dtype.type = T_BOOL;
+    yylval.dtype.type = T_BOOL;
+num_common:
     yylval.dtype.val = NewString(Scanner_text(scan));
     yylval.dtype.bitfield = 0;
     yylval.dtype.throws = 0;
diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y
index 96bf535..9074114 100644
--- a/Source/CParse/parser.y
+++ b/Source/CParse/parser.y
@@ -1652,7 +1652,7 @@
 %token <id> STRING WSTRING
 %token <loc> INCLUDE IMPORT INSERT
 %token <str> CHARCONST WCHARCONST
-%token <dtype> NUM_INT NUM_FLOAT NUM_UNSIGNED NUM_LONG NUM_ULONG NUM_LONGLONG NUM_ULONGLONG NUM_BOOL
+%token <dtype> NUM_INT NUM_DOUBLE NUM_FLOAT NUM_LONGDOUBLE NUM_UNSIGNED NUM_LONG NUM_ULONG NUM_LONGLONG NUM_ULONGLONG NUM_BOOL
 %token <intvalue> TYPEDEF
 %token <type> TYPE_INT TYPE_UNSIGNED TYPE_SHORT TYPE_LONG TYPE_FLOAT TYPE_DOUBLE TYPE_CHAR TYPE_WCHAR TYPE_VOID TYPE_SIGNED TYPE_BOOL TYPE_COMPLEX TYPE_TYPEDEF TYPE_RAW TYPE_NON_ISO_INT8 TYPE_NON_ISO_INT16 TYPE_NON_ISO_INT32 TYPE_NON_ISO_INT64
 %token LPAREN RPAREN COMMA SEMI EXTERN INIT LBRACE RBRACE PERIOD ELLIPSIS
@@ -1772,8 +1772,13 @@
   Node *n = Swig_symbol_clookup(dtype->val, 0);
   if (n) {
     return Getattr(n, "type");
-  } else if (Equal(dtype->val, "true") || Equal(dtype->val, "false")) {
-    return NewString("bool");
+  } 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).
+     */
+    return NewSwigType(dtype->type);
   } else {
     return NULL;
   }
@@ -5350,7 +5355,7 @@
 		 if (skip_balanced('{','}') < 0) Exit(EXIT_FAILURE);
 		 $$.val = NewString(scanner_ccode);
 		 $$.rawval = 0;
-                 $$.type = T_INT;
+                 $$.type = T_INT; /* This may be a lie. */
 		 $$.bitfield = 0;
 		 $$.throws = 0;
 		 $$.throwf = 0;
@@ -5370,7 +5375,7 @@
                | empty {
                  $$.val = 0;
                  $$.rawval = 0;
-                 $$.type = T_INT;
+                 $$.type = T_INT; /* This is a lie. */
 		 $$.bitfield = 0;
 		 $$.throws = 0;
 		 $$.throwf = 0;
@@ -6628,7 +6633,7 @@
                | type {
 		 Node *n;
 		 $$.val = $1;
-		 $$.type = T_INT;
+		 $$.type = T_INT; /* This may be a lie. */
 		 /* Check if value is in scope */
 		 n = Swig_symbol_clookup($1,0);
 		 if (n) {
@@ -6835,7 +6840,9 @@
 	       ;
 
 exprnum        :  NUM_INT { $$ = $1; }
+               |  NUM_DOUBLE { $$ = $1; }
                |  NUM_FLOAT { $$ = $1; }
+               |  NUM_LONGDOUBLE { $$ = $1; }
                |  NUM_UNSIGNED { $$ = $1; }
                |  NUM_LONG { $$ = $1; }
                |  NUM_ULONG { $$ = $1; }
diff --git a/Source/Preprocessor/expr.c b/Source/Preprocessor/expr.c
index 95e05cf..c13a75e 100644
--- a/Source/Preprocessor/expr.c
+++ b/Source/Preprocessor/expr.c
@@ -355,7 +355,7 @@
 	stack[sp].value = 0;
 	stack[sp].svalue = 0;
 	stack[sp].op = EXPR_VALUE;
-      } else if ((token == SWIG_TOKEN_FLOAT) || (token == SWIG_TOKEN_DOUBLE)) {
+      } else if (token == SWIG_TOKEN_FLOAT || token == SWIG_TOKEN_DOUBLE || token == SWIG_TOKEN_LONGDOUBLE) {
 	errmsg = "Floating point constant in preprocessor expression";
 	*error = 1;
 	return 0;
diff --git a/Source/Swig/scanner.c b/Source/Swig/scanner.c
index 7ef3293..63d38ec 100644
--- a/Source/Swig/scanner.c
+++ b/Source/Swig/scanner.c
@@ -1121,7 +1121,7 @@
 	return SWIG_TOKEN_FLOAT;
       } else if ((c == 'l') || (c == 'L')) {
 	Delitem(s->text, DOH_END);
-	return SWIG_TOKEN_DOUBLE;
+	return SWIG_TOKEN_LONGDOUBLE;
       } else {
 	retract(s, 1);
 	return (SWIG_TOKEN_DOUBLE);
@@ -1258,7 +1258,7 @@
 	return SWIG_TOKEN_FLOAT;
       } else if ((c == 'l') || (c == 'L')) {
 	Delitem(s->text, DOH_END);
-	return SWIG_TOKEN_DOUBLE;
+	return SWIG_TOKEN_LONGDOUBLE;
       } else {
 	retract(s, 1);
 	return SWIG_TOKEN_DOUBLE;
diff --git a/Source/Swig/stype.c b/Source/Swig/stype.c
index 5085719..fdc83ca 100644
--- a/Source/Swig/stype.c
+++ b/Source/Swig/stype.c
@@ -134,6 +134,9 @@
   case T_DOUBLE:
     return NewString("double");
     break;
+  case T_LONGDOUBLE:
+    return NewString("long double");
+    break;
   case T_COMPLEX:
     return NewString("_Complex");
     break;
diff --git a/Source/Swig/swigscan.h b/Source/Swig/swigscan.h
index 6c9d1ff..08c9085 100644
--- a/Source/Swig/swigscan.h
+++ b/Source/Swig/swigscan.h
@@ -53,26 +53,27 @@
 #define   SWIG_TOKEN_ID           15       /* identifier */
 #define   SWIG_TOKEN_FLOAT        16       /* 3.1415F */
 #define   SWIG_TOKEN_DOUBLE       17       /* 3.1415 */
-#define   SWIG_TOKEN_INT          18       /* 314 */
-#define   SWIG_TOKEN_UINT         19       /* 314U */
-#define   SWIG_TOKEN_LONG         20       /* 314L */
-#define   SWIG_TOKEN_ULONG        21       /* 314UL */
-#define   SWIG_TOKEN_CHAR         22       /* 'charconst' */
-#define   SWIG_TOKEN_PERIOD       23       /* . */
-#define   SWIG_TOKEN_AT           24       /* @ */
-#define   SWIG_TOKEN_DOLLAR       25       /* $ */
-#define   SWIG_TOKEN_CODEBLOCK    26       /* %{ ... %} ... */
-#define   SWIG_TOKEN_RSTRING      27       /* `charconst` */
-#define   SWIG_TOKEN_LONGLONG     28       /* 314LL */
-#define   SWIG_TOKEN_ULONGLONG    29       /* 314ULL */
-#define   SWIG_TOKEN_QUESTION     30       /* ? */
-#define   SWIG_TOKEN_COMMENT      31       /* C or C++ comment */
-#define   SWIG_TOKEN_BOOL         32       /* true or false */
-#define   SWIG_TOKEN_WSTRING      33       /* L"str" */
-#define   SWIG_TOKEN_WCHAR        34       /* L'c' */
-#define   SWIG_TOKEN_ELLIPSIS     35       /* ... */
-#define   SWIG_TOKEN_LLBRACKET    36       /* [[ */
-#define   SWIG_TOKEN_RRBRACKET    37       /* ]] */
+#define   SWIG_TOKEN_LONGDOUBLE   18       /* 3.1415L */
+#define   SWIG_TOKEN_INT          19       /* 314 */
+#define   SWIG_TOKEN_UINT         20       /* 314U */
+#define   SWIG_TOKEN_LONG         21       /* 314L */
+#define   SWIG_TOKEN_ULONG        22       /* 314UL */
+#define   SWIG_TOKEN_CHAR         23       /* 'charconst' */
+#define   SWIG_TOKEN_PERIOD       24       /* . */
+#define   SWIG_TOKEN_AT           25       /* @ */
+#define   SWIG_TOKEN_DOLLAR       26       /* $ */
+#define   SWIG_TOKEN_CODEBLOCK    27       /* %{ ... %} ... */
+#define   SWIG_TOKEN_RSTRING      28       /* `charconst` */
+#define   SWIG_TOKEN_LONGLONG     29       /* 314LL */
+#define   SWIG_TOKEN_ULONGLONG    30       /* 314ULL */
+#define   SWIG_TOKEN_QUESTION     31       /* ? */
+#define   SWIG_TOKEN_COMMENT      32       /* C or C++ comment */
+#define   SWIG_TOKEN_BOOL         33       /* true or false */
+#define   SWIG_TOKEN_WSTRING      34       /* L"str" */
+#define   SWIG_TOKEN_WCHAR        35       /* L'c' */
+#define   SWIG_TOKEN_ELLIPSIS     36       /* ... */
+#define   SWIG_TOKEN_LLBRACKET    37       /* [[ */
+#define   SWIG_TOKEN_RRBRACKET    38       /* ]] */
 
 #define   SWIG_TOKEN_ILLEGAL      99
 #define   SWIG_TOKEN_ERROR        -1