OSDN Git Service

2002-05-27 H.J. Lu (hjl@gnu.org)
[pf3gnuchains/gcc-fork.git] / gcc / c-lex.c
index 3a80a74..292c267 100644 (file)
@@ -23,12 +23,12 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "system.h"
 
 #include "rtl.h"
-#include "expr.h"
 #include "tree.h"
+#include "expr.h"
 #include "input.h"
 #include "output.h"
-#include "c-lex.h"
 #include "c-tree.h"
+#include "c-common.h"
 #include "flags.h"
 #include "timevar.h"
 #include "cpplib.h"
@@ -39,13 +39,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "splay-tree.h"
 #include "debug.h"
 
-/* MULTIBYTE_CHARS support only works for native compilers.
-   ??? Ideally what we want is to model widechar support after
-   the current floating point support.  */
-#ifdef CROSS_COMPILE
-#undef MULTIBYTE_CHARS
-#endif
-
 #ifdef MULTIBYTE_CHARS
 #include "mbchar.h"
 #include <locale.h>
@@ -54,9 +47,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #define GET_ENVIRONMENT(ENV_VALUE,ENV_NAME) ((ENV_VALUE) = getenv (ENV_NAME))
 #endif
 
-/* The input filename as understood by CPP, where "" represents stdin.  */
-static const char *cpp_filename;
-
 /* The current line map.  */
 static const struct line_map *map;
 
@@ -67,9 +57,6 @@ static unsigned int src_lineno;
 static int header_time, body_time;
 static splay_tree file_info_tree;
 
-/* Cause the `yydebug' variable to be defined.  */
-#define YYDEBUG 1
-
 /* File used for outputting assembler code.  */
 extern FILE *asm_out_file;
 
@@ -79,16 +66,15 @@ extern FILE *asm_out_file;
 /* Number of bytes in a wide character.  */
 #define WCHAR_BYTES (WCHAR_TYPE_SIZE / BITS_PER_UNIT)
 
-int indent_level;        /* Number of { minus number of }.  */
 int pending_lang_change; /* If we need to switch languages - C++ only */
 int c_header_level;     /* depth in C headers - C++ only */
 
 /* Nonzero tells yylex to ignore \ in string constants.  */
 static int ignore_escape_flag;
 
-static void parse_float                PARAMS ((PTR));
 static tree lex_number         PARAMS ((const char *, unsigned int));
-static tree lex_string         PARAMS ((const char *, unsigned int, int));
+static tree lex_string         PARAMS ((const unsigned char *, unsigned int,
+                                        int));
 static tree lex_charconst      PARAMS ((const cpp_token *));
 static void update_header_times        PARAMS ((const char *));
 static int dump_one_header     PARAMS ((splay_tree_node, void *));
@@ -109,7 +95,7 @@ init_c_lex (filename)
   struct cpp_callbacks *cb;
   struct c_fileinfo *toplevel;
 
-  /* Set up filename timing.  Must happen before cpp_start_read.  */
+  /* Set up filename timing.  Must happen before cpp_read_main_file.  */
   file_info_tree = splay_tree_new ((splay_tree_compare_fn)strcmp,
                                   0,
                                   (splay_tree_delete_value_fn)free);
@@ -136,34 +122,42 @@ init_c_lex (filename)
 
   /* Set the debug callbacks if we can use them.  */
   if (debug_info_level == DINFO_LEVEL_VERBOSE
-      && (write_symbols == DWARF_DEBUG || write_symbols == DWARF2_DEBUG))
+      && (write_symbols == DWARF_DEBUG || write_symbols == DWARF2_DEBUG
+          || write_symbols == VMS_AND_DWARF2_DEBUG))
     {
       cb->define = cb_define;
       cb->undef = cb_undef;
     }
 
-
-  if (filename == 0 || !strcmp (filename, "-"))
-    filename = "stdin", cpp_filename = "";
-  else
-    cpp_filename = filename;
-
   /* Start it at 0.  */
   lineno = 0;
 
-  return filename;
+  if (filename == NULL || !strcmp (filename, "-"))
+    filename = "";
+
+  return cpp_read_main_file (parse_in, filename, ident_hash);
 }
 
 /* A thin wrapper around the real parser that initializes the 
-   integrated preprocessor after debug output has been initialized.  */
+   integrated preprocessor after debug output has been initialized.
+   Also, make sure the start_source_file debug hook gets called for
+   the primary source file.  */
 
-int
-yyparse()
+void
+c_common_parse_file (set_yydebug)
+     int set_yydebug ATTRIBUTE_UNUSED;
 {
-  if (! cpp_start_read (parse_in, cpp_filename))
-    return 1;                  /* cpplib has emitted an error.  */
+#if YYDEBUG != 0
+  yydebug = set_yydebug;
+#else
+  warning ("YYDEBUG not defined");
+#endif
+
+  (*debug_hooks->start_source_file) (lineno, input_filename);
+  cpp_finish_options (parse_in);
 
-  return yyparse_1();
+  yyparse ();
+  free_parser_stacks ();
 }
 
 struct c_fileinfo *
@@ -229,9 +223,6 @@ dump_time_statistics ()
   splay_tree_foreach (file_info_tree, dump_one_header, 0);
 }
 
-/* Not yet handled: #pragma, #define, #undef.
-   No need to deal with linemarkers under normal conditions.  */
-
 static void
 cb_ident (pfile, line, str)
      cpp_reader *pfile ATTRIBUTE_UNUSED;
@@ -242,7 +233,7 @@ cb_ident (pfile, line, str)
   if (! flag_no_ident)
     {
       /* Convert escapes in the string.  */
-      tree value = lex_string ((const char *)str->text, str->len, 0);
+      tree value = lex_string (str->text, str->len, 0);
       ASM_OUTPUT_IDENT (asm_out_file, TREE_STRING_POINTER (value));
     }
 #endif
@@ -274,10 +265,11 @@ cb_file_change (pfile, new_map)
        main_input_filename = new_map->to_file;
       else
        {
-         lineno = SOURCE_LINE (new_map - 1, new_map->from_line - 1);
+          int included_at = SOURCE_LINE (new_map - 1, new_map->from_line - 1);
+
+         lineno = included_at;
          push_srcloc (new_map->to_file, 1);
-         input_file_stack->indent_level = indent_level;
-         (*debug_hooks->start_source_file) (lineno, new_map->to_file);
+         (*debug_hooks->start_source_file) (included_at, new_map->to_file);
 #ifndef NO_IMPLICIT_EXTERN_C
          if (c_header_level)
            ++c_header_level;
@@ -299,16 +291,6 @@ cb_file_change (pfile, new_map)
          --pending_lang_change;
        }
 #endif
-#if 0
-      if (indent_level != input_file_stack->indent_level)
-       {
-         warning_with_file_and_line
-           (input_filename, lineno,
-            "This file contains more '%c's than '%c's.",
-            indent_level > input_file_stack->indent_level ? '{' : '}',
-            indent_level > input_file_stack->indent_level ? '}' : '{');
-       }
-#endif
       pop_srcloc ();
       
       (*debug_hooks->end_source_file) (to_line);
@@ -335,13 +317,13 @@ cb_def_pragma (pfile, line)
   if (warn_unknown_pragmas > in_system_header)
     {
       const unsigned char *space, *name = 0;
-      cpp_token s;
+      const cpp_token *s;
 
-      cpp_get_token (pfile, &s);
-      space = cpp_token_as_text (pfile, &s);
-      cpp_get_token (pfile, &s);
-      if (s.type == CPP_NAME)
-       name = cpp_token_as_text (pfile, &s);
+      s = cpp_get_token (pfile);
+      space = cpp_token_as_text (pfile, s);
+      s = cpp_get_token (pfile);
+      if (s->type == CPP_NAME)
+       name = cpp_token_as_text (pfile, s);
 
       lineno = SOURCE_LINE (map, line);
       if (name)
@@ -685,10 +667,10 @@ utf8_extend_token (c)
 #if 0
 struct try_type
 {
-  tree *node_var;
-  char unsigned_flag;
-  char long_flag;
-  char long_long_flag;
+  tree *const node_var;
+  const char unsigned_flag;
+  const char long_flag;
+  const char long_long_flag;
 };
 
 struct try_type type_sequence[] =
@@ -702,77 +684,17 @@ struct try_type type_sequence[] =
 };
 #endif /* 0 */
 \f
-struct pf_args
-{
-  /* Input */
-  const char *str;
-  int fflag;
-  int lflag;
-  int base;
-  /* Output */
-  int conversion_errno;
-  REAL_VALUE_TYPE value;
-  tree type;
-};
-static void
-parse_float (data)
-  PTR data;
-{
-  struct pf_args * args = (struct pf_args *) data;
-  const char *typename;
-
-  args->conversion_errno = 0;
-  args->type = double_type_node;
-  typename = "double";
-
-  /* The second argument, machine_mode, of REAL_VALUE_ATOF
-     tells the desired precision of the binary result
-     of decimal-to-binary conversion.  */
-
-  if (args->fflag)
-    {
-      if (args->lflag)
-       error ("both 'f' and 'l' suffixes on floating constant");
-
-      args->type = float_type_node;
-      typename = "float";
-    }
-  else if (args->lflag)
-    {
-      args->type = long_double_type_node;
-      typename = "long double";
-    }
-  else if (flag_single_precision_constant)
-    {
-      args->type = float_type_node;
-      typename = "float";
-    }
-
-  errno = 0;
-  if (args->base == 16)
-    args->value = REAL_VALUE_HTOF (args->str, TYPE_MODE (args->type));
-  else
-    args->value = REAL_VALUE_ATOF (args->str, TYPE_MODE (args->type));
-
-  args->conversion_errno = errno;
-  /* A diagnostic is required here by some ISO C testsuites.
-     This is not pedwarn, because some people don't want
-     an error for this.  */
-  if (REAL_VALUE_ISINF (args->value) && pedantic)
-    warning ("floating point number exceeds range of '%s'", typename);
-}
 int
 c_lex (value)
      tree *value;
 {
-  cpp_token tok;
-  enum cpp_ttype type;
+  const cpp_token *tok;
 
   retry:
   timevar_push (TV_CPP);
-  cpp_get_token (parse_in, &tok);
+  do
+    tok = cpp_get_token (parse_in);
+  while (tok->type == CPP_PADDING);
   timevar_pop (TV_CPP);
 
   /* The C++ front end does horrible things with the current line
@@ -781,37 +703,33 @@ c_lex (value)
   lineno = src_lineno;
 
   *value = NULL_TREE;
-  type = tok.type;
-  switch (type)
+  switch (tok->type)
     {
-    case CPP_OPEN_BRACE:  indent_level++;  break;
-    case CPP_CLOSE_BRACE: indent_level--;  break;
-
-    /* Issue this error here, where we can get at tok.val.c.  */
+    /* Issue this error here, where we can get at tok->val.c.  */
     case CPP_OTHER:
-      if (ISGRAPH (tok.val.c))
-       error ("stray '%c' in program", tok.val.c);
+      if (ISGRAPH (tok->val.c))
+       error ("stray '%c' in program", tok->val.c);
       else
-       error ("stray '\\%o' in program", tok.val.c);
+       error ("stray '\\%o' in program", tok->val.c);
       goto retry;
       
     case CPP_NAME:
-      *value = HT_IDENT_TO_GCC_IDENT (HT_NODE (tok.val.node));
+      *value = HT_IDENT_TO_GCC_IDENT (HT_NODE (tok->val.node));
       break;
 
     case CPP_NUMBER:
-      *value = lex_number ((const char *)tok.val.str.text, tok.val.str.len);
+      *value = lex_number ((const char *)tok->val.str.text, tok->val.str.len);
       break;
 
     case CPP_CHAR:
     case CPP_WCHAR:
-      *value = lex_charconst (&tok);
+      *value = lex_charconst (tok);
       break;
 
     case CPP_STRING:
     case CPP_WSTRING:
-      *value = lex_string ((const char *)tok.val.str.text,
-                          tok.val.str.len, tok.type == CPP_WSTRING);
+      *value = lex_string (tok->val.str.text, tok->val.str.len,
+                          tok->type == CPP_WSTRING);
       break;
 
       /* These tokens should not be visible outside cpplib.  */
@@ -823,7 +741,7 @@ c_lex (value)
     default: break;
     }
 
-  return type;
+  return tok->type;
 }
 
 #define ERROR(msgid) do { error(msgid); goto syntax_error; } while(0)
@@ -910,9 +828,10 @@ lex_number (str, len)
          /* It is not a decimal point.
             It should be a digit (perhaps a hex digit).  */
 
-         if (ISDIGIT (c))
+         if (ISDIGIT (c)
+             || (base == 16 && ISXDIGIT (c)))
            {
-             n = c - '0';
+             n = hex_value (c);
            }
          else if (base <= 10 && (c == 'e' || c == 'E'))
            {
@@ -925,14 +844,6 @@ lex_number (str, len)
              floatflag = AFTER_EXPON;
              break;   /* start of exponent */
            }
-         else if (base == 16 && c >= 'a' && c <= 'f')
-           {
-             n = c - 'a' + 10;
-           }
-         else if (base == 16 && c >= 'A' && c <= 'F')
-           {
-             n = c - 'A' + 10;
-           }
          else
            {
              p--;
@@ -980,14 +891,11 @@ lex_number (str, len)
   if (floatflag != NOT_FLOAT)
     {
       tree type;
-      int imag, fflag, lflag, conversion_errno;
+      const char *typename;
+      int imag, fflag, lflag;
       REAL_VALUE_TYPE real;
-      struct pf_args args;
       char *copy;
 
-      if (base == 16 && pedantic && !flag_isoc99)
-       pedwarn ("floating constant may not be in radix 16");
-
       if (base == 16 && floatflag != AFTER_EXPON)
        ERROR ("hexadecimal floating constant has no exponent");
 
@@ -1052,34 +960,45 @@ lex_number (str, len)
            ERROR ("invalid suffix on floating constant");
          }
 
-      /* Setup input for parse_float() */
-      args.str = copy;
-      args.fflag = fflag;
-      args.lflag = lflag;
-      args.base = base;
+      type = double_type_node;
+      typename = "double";
+       
+      if (fflag)
+       {
+         if (lflag)
+           ERROR ("both 'f' and 'l' suffixes on floating constant");
 
-      /* Convert string to a double, checking for overflow.  */
-      if (do_float_handler (parse_float, (PTR) &args))
+         type = float_type_node;
+         typename = "float";
+       }
+      else if (lflag)
        {
-         /* Receive output from parse_float() */
-         real = args.value;
+         type = long_double_type_node;
+         typename = "long double";
        }
+      else if (flag_single_precision_constant)
+       {
+         type = float_type_node;
+         typename = "float";
+       }
+
+      /* Warn about this only after we know we're not issuing an error.  */
+      if (base == 16 && pedantic && !flag_isoc99)
+       pedwarn ("hexadecimal floating constants are only valid in C99");
+
+      /* The second argument, machine_mode, of REAL_VALUE_ATOF
+        tells the desired precision of the binary result
+        of decimal-to-binary conversion.  */
+      if (base == 16)
+       real = REAL_VALUE_HTOF (copy, TYPE_MODE (type));
       else
-         /* We got an exception from parse_float() */
-         ERROR ("floating constant out of range");
-
-      /* Receive output from parse_float() */
-      conversion_errno = args.conversion_errno;
-      type = args.type;
-           
-#ifdef ERANGE
-      /* ERANGE is also reported for underflow,
-        so test the value to distinguish overflow from that.  */
-      if (conversion_errno == ERANGE && !flag_traditional && pedantic
-         && (REAL_VALUES_LESS (dconst1, real)
-             || REAL_VALUES_LESS (real, dconstm1)))
+       real = REAL_VALUE_ATOF (copy, TYPE_MODE (type));
+
+      /* A diagnostic is required here by some ISO C testsuites.
+        This is not pedwarn, because some people don't want
+        an error for this.  */
+      if (REAL_VALUE_ISINF (real) && pedantic)
        warning ("floating point number exceeds range of 'double'");
-#endif
 
       /* Create a node with determined type and value.  */
       if (imag)
@@ -1090,7 +1009,7 @@ lex_number (str, len)
     }
   else
     {
-      tree trad_type, ansi_type, type;
+      tree trad_type, type;
       HOST_WIDE_INT high, low;
       int spec_unsigned = 0;
       int spec_long = 0;
@@ -1099,7 +1018,7 @@ lex_number (str, len)
       int suffix_lu = 0;
       int warn = 0, i;
 
-      trad_type = ansi_type = type = NULL_TREE;
+      trad_type = type = NULL_TREE;
       while (p < str + len)
        {
          c = *p++;
@@ -1171,11 +1090,9 @@ lex_number (str, len)
       TREE_TYPE (value) = long_long_unsigned_type_node;
 
       /* If warn_traditional, calculate both the ISO type and the
-        traditional type, then see if they disagree.
-        Otherwise, calculate only the type for the dialect in use.  */
-      if (warn_traditional || flag_traditional)
+        traditional type, then see if they disagree.  */
+      if (warn_traditional)
        {
-         /* Calculate the traditional type.  */
          /* Traditionally, any constant is signed; but if unsigned is
             specified explicitly, obey that.  Use the smallest size
             with the right number of bits, except for one special
@@ -1205,50 +1122,46 @@ lex_number (str, len)
                         ? widest_unsigned_literal_type_node
                         : widest_integer_literal_type_node);
        }
-      if (warn_traditional || ! flag_traditional)
-       {
-         /* Calculate the ISO type.  */
-         if (! spec_long && ! spec_unsigned
-             && int_fits_type_p (value, integer_type_node))
-           ansi_type = integer_type_node;
-         else if (! spec_long && (base != 10 || spec_unsigned)
-                  && int_fits_type_p (value, unsigned_type_node))
-           ansi_type = unsigned_type_node;
-         else if (! spec_unsigned && !spec_long_long
-                  && int_fits_type_p (value, long_integer_type_node))
-           ansi_type = long_integer_type_node;
-         else if (! spec_long_long
-                  && int_fits_type_p (value, long_unsigned_type_node))
-           ansi_type = long_unsigned_type_node;
-         else if (! spec_unsigned
-                  && int_fits_type_p (value, long_long_integer_type_node))
-           ansi_type = long_long_integer_type_node;
-         else if (int_fits_type_p (value, long_long_unsigned_type_node))
-           ansi_type = long_long_unsigned_type_node;
-         else if (! spec_unsigned
-                  && int_fits_type_p (value, widest_integer_literal_type_node))
-           ansi_type = widest_integer_literal_type_node;
-         else
-           ansi_type = widest_unsigned_literal_type_node;
-       }
-
-      type = flag_traditional ? trad_type : ansi_type;
+       
+       /* Calculate the ISO type.  */
+       if (! spec_long && ! spec_unsigned
+           && int_fits_type_p (value, integer_type_node))
+         type = integer_type_node;
+       else if (! spec_long && (base != 10 || spec_unsigned)
+                && int_fits_type_p (value, unsigned_type_node))
+         type = unsigned_type_node;
+       else if (! spec_unsigned && !spec_long_long
+                && int_fits_type_p (value, long_integer_type_node))
+         type = long_integer_type_node;
+       else if (! spec_long_long
+                && int_fits_type_p (value, long_unsigned_type_node))
+         type = long_unsigned_type_node;
+       else if (! spec_unsigned
+                && int_fits_type_p (value, long_long_integer_type_node))
+         type = long_long_integer_type_node;
+       else if (int_fits_type_p (value, long_long_unsigned_type_node))
+         type = long_long_unsigned_type_node;
+       else if (! spec_unsigned
+                && int_fits_type_p (value, widest_integer_literal_type_node))
+         type = widest_integer_literal_type_node;
+       else
+         type = widest_unsigned_literal_type_node;
 
       /* We assume that constants specified in a non-decimal
         base are bit patterns, and that the programmer really
         meant what they wrote.  */
       if (warn_traditional && !in_system_header
-         && base == 10 && trad_type != ansi_type)
+         && base == 10 && trad_type != type)
        {
-         if (TYPE_PRECISION (trad_type) != TYPE_PRECISION (ansi_type))
-           warning ("width of integer constant changes with -traditional");
-         else if (TREE_UNSIGNED (trad_type) != TREE_UNSIGNED (ansi_type))
-           warning ("integer constant is unsigned in ISO C, signed with -traditional");
+         if (TYPE_PRECISION (trad_type) != TYPE_PRECISION (type))
+           warning ("width of integer constant is different in traditional C");
+         else if (TREE_UNSIGNED (trad_type) != TREE_UNSIGNED (type))
+           warning ("integer constant is unsigned in ISO C, signed in traditional C");
          else
-           warning ("width of integer constant may change on other systems with -traditional");
+           warning ("width of integer constant may change on other systems in traditional C");
        }
 
-      if (pedantic && !flag_traditional && (flag_isoc99 || !spec_long_long)
+      if (pedantic && (flag_isoc99 || !spec_long_long)
          && !warn
          && ((flag_isoc99
               ? TYPE_PRECISION (long_long_integer_type_node)
@@ -1258,9 +1171,9 @@ lex_number (str, len)
          pedwarn ("integer constant larger than the maximum value of %s",
                   (flag_isoc99
                    ? (TREE_UNSIGNED (type)
-                      ? "an unsigned long long int"
-                      : "a long long int")
-                   : "an unsigned long int"));
+                      ? _("an unsigned long long int")
+                      : _("a long long int"))
+                   : _("an unsigned long int")));
        }
 
       if (base == 10 && ! spec_unsigned && TREE_UNSIGNED (type))
@@ -1275,15 +1188,6 @@ lex_number (str, len)
          else
            ERROR ("complex integer constant is too wide for 'complex int'");
        }
-      else if (flag_traditional && !int_fits_type_p (value, type))
-       /* The traditional constant 0x80000000 is signed
-          but doesn't fit in the range of int.
-          This will change it to -0x80000000, which does fit.  */
-       {
-         TREE_TYPE (value) = unsigned_type (type);
-         value = convert (type, value);
-         TREE_OVERFLOW (value) = TREE_CONSTANT_OVERFLOW (value) = 0;
-       }
       else
        TREE_TYPE (value) = type;
 
@@ -1307,17 +1211,15 @@ lex_number (str, len)
 
 static tree
 lex_string (str, len, wide)
-     const char *str;
+     const unsigned char *str;
      unsigned int len;
      int wide;
 {
   tree value;
   char *buf = alloca ((len + 1) * (wide ? WCHAR_BYTES : 1));
   char *q = buf;
-  const char *p = str, *limit = str + len;
-  unsigned int c;
-  unsigned width = wide ? WCHAR_TYPE_SIZE
-                       : TYPE_PRECISION (char_type_node);
+  const unsigned char *p = str, *limit = str + len;
+  cppchar_t c;
 
 #ifdef MULTIBYTE_CHARS
   /* Reset multibyte conversion state.  */
@@ -1330,10 +1232,10 @@ lex_string (str, len, wide)
       wchar_t wc;
       int char_len;
 
-      char_len = local_mbtowc (&wc, p, limit - p);
+      char_len = local_mbtowc (&wc, (const char *) p, limit - p);
       if (char_len == -1)
        {
-         warning ("Ignoring invalid multibyte character");
+         warning ("ignoring invalid multibyte character");
          char_len = 1;
          c = *p++;
        }
@@ -1347,20 +1249,10 @@ lex_string (str, len, wide)
 #endif
 
       if (c == '\\' && !ignore_escape_flag)
-       {
-         unsigned int mask;
-
-         if (width < HOST_BITS_PER_INT)
-           mask = ((unsigned int) 1 << width) - 1;
-         else
-           mask = ~0;
-         c = cpp_parse_escape (parse_in, (const unsigned char **) &p,
-                               (const unsigned char *) limit,
-                               mask, flag_traditional);
-       }
+       c = cpp_parse_escape (parse_in, &p, limit, wide);
        
-      /* Add this single character into the buffer either as a wchar_t
-        or as a single byte.  */
+      /* Add this single character into the buffer either as a wchar_t,
+        a multibyte sequence, or as a single byte.  */
       if (wide)
        {
          unsigned charwidth = TYPE_PRECISION (char_type_node);
@@ -1381,6 +1273,16 @@ lex_string (str, len, wide)
            }
          q += WCHAR_BYTES;
        }
+#ifdef MULTIBYTE_CHARS
+      else if (char_len > 1)
+       {
+         /* We're dealing with a multibyte character. */
+         for ( ; char_len >0; --char_len)
+           {
+             *q++ = *(p - char_len);
+           }
+       }
+#endif
       else
        {
          *q++ = c;
@@ -1414,31 +1316,31 @@ static tree
 lex_charconst (token)
      const cpp_token *token;
 {
-  HOST_WIDE_INT result;
-  tree value;
+  cppchar_t result;
+  tree type, value;
   unsigned int chars_seen;
+  int unsignedp;
  
-  result = cpp_interpret_charconst (parse_in, token, warn_multichar,
-                                   flag_traditional, &chars_seen);
+  result = cpp_interpret_charconst (parse_in, token,
+                                   &chars_seen, &unsignedp);
+
+  /* Cast to cppchar_signed_t to get correct sign-extension of RESULT
+     before possibly widening to HOST_WIDE_INT for build_int_2.  */
+  if (unsignedp || (cppchar_signed_t) result >= 0)
+    value = build_int_2 (result, 0);
+  else
+    value = build_int_2 ((cppchar_signed_t) result, -1);
+
   if (token->type == CPP_WCHAR)
-    {
-      value = build_int_2 (result, 0);
-      TREE_TYPE (value) = wchar_type_node;
-    }
+    type = wchar_type_node;
+  /* In C, a character constant has type 'int'.
+     In C++ 'char', but multi-char charconsts have type 'int'.  */
+  else if ((c_language == clk_c || c_language == clk_objective_c)
+          || chars_seen > 1)
+    type = integer_type_node;
   else
-    {
-      if (result < 0)
-       value = build_int_2 (result, -1);
-      else
-       value = build_int_2 (result, 0);
-      /* In C, a character constant has type 'int'.
-        In C++ 'char', but multi-char charconsts have type 'int'.  */
-      if (c_language == clk_cplusplus && chars_seen <= 1)
-       TREE_TYPE (value) = char_type_node;
-      else
-       TREE_TYPE (value) = integer_type_node;
-    }
+    type = char_type_node;
+
+  TREE_TYPE (value) = type;
   return value;
 }