OSDN Git Service

2009-10-14 Sebastian Pop <sebastian.pop@amd.com>
[pf3gnuchains/gcc-fork.git] / libcpp / expr.c
index 59de8ef..96dd2fd 100644 (file)
@@ -1,11 +1,11 @@
 /* Parse C expressions for cpplib.
    Copyright (C) 1987, 1992, 1994, 1995, 1997, 1998, 1999, 2000, 2001,
 /* Parse C expressions for cpplib.
    Copyright (C) 1987, 1992, 1994, 1995, 1997, 1998, 1999, 2000, 2001,
-   2002, 2004 Free Software Foundation.
+   2002, 2004, 2008, 2009 Free Software Foundation.
    Contributed by Per Bothner, 1994.
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
    Contributed by Per Bothner, 1994.
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
+Free Software Foundation; either version 3, or (at your option) any
 later version.
 
 This program is distributed in the hope that it will be useful,
 later version.
 
 This program is distributed in the hope that it will be useful,
@@ -14,9 +14,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.  */
+along with this program; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
 
 #include "config.h"
 #include "system.h"
@@ -32,6 +31,7 @@ struct op
 {
   const cpp_token *token;      /* The token forming op (for diagnostics).  */
   cpp_num value;               /* The value logically "right" of op.  */
 {
   const cpp_token *token;      /* The token forming op (for diagnostics).  */
   cpp_num value;               /* The value logically "right" of op.  */
+  source_location loc;          /* The location of this value.         */
   enum cpp_ttype op;
 };
 
   enum cpp_ttype op;
 };
 
@@ -52,7 +52,8 @@ static cpp_num num_inequality_op (cpp_reader *, cpp_num, cpp_num,
 static cpp_num num_equality_op (cpp_reader *, cpp_num, cpp_num,
                                enum cpp_ttype);
 static cpp_num num_mul (cpp_reader *, cpp_num, cpp_num);
 static cpp_num num_equality_op (cpp_reader *, cpp_num, cpp_num,
                                enum cpp_ttype);
 static cpp_num num_mul (cpp_reader *, cpp_num, cpp_num);
-static cpp_num num_div_op (cpp_reader *, cpp_num, cpp_num, enum cpp_ttype);
+static cpp_num num_div_op (cpp_reader *, cpp_num, cpp_num, enum cpp_ttype,
+                          source_location);
 static cpp_num num_lshift (cpp_num, size_t, size_t);
 static cpp_num num_rshift (cpp_num, size_t, size_t);
 
 static cpp_num num_lshift (cpp_num, size_t, size_t);
 static cpp_num num_rshift (cpp_num, size_t, size_t);
 
@@ -82,39 +83,106 @@ static void check_promotion (cpp_reader *, const struct op *);
 static unsigned int
 interpret_float_suffix (const uchar *s, size_t len)
 {
 static unsigned int
 interpret_float_suffix (const uchar *s, size_t len)
 {
-  size_t f = 0, l = 0, i = 0, d = 0;
+  size_t flags;
+  size_t f, d, l, w, q, i;
 
 
-  while (len--)
-    switch (s[len])
+  flags = 0;
+  f = d = l = w = q = i = 0;
+
+  /* Process decimal float suffixes, which are two letters starting
+     with d or D.  Order and case are significant.  */
+  if (len == 2 && (*s == 'd' || *s == 'D'))
+    {
+      bool uppercase = (*s == 'D');
+      switch (s[1])
       {
       {
-      case 'f': case 'F':
-       if (d > 0)
-         return 0;
-       f++;
+      case 'f': return (!uppercase ? (CPP_N_DFLOAT | CPP_N_SMALL): 0); break;
+      case 'F': return (uppercase ? (CPP_N_DFLOAT | CPP_N_SMALL) : 0); break;
+      case 'd': return (!uppercase ? (CPP_N_DFLOAT | CPP_N_MEDIUM): 0); break;
+      case 'D': return (uppercase ? (CPP_N_DFLOAT | CPP_N_MEDIUM) : 0); break;
+      case 'l': return (!uppercase ? (CPP_N_DFLOAT | CPP_N_LARGE) : 0); break;
+      case 'L': return (uppercase ? (CPP_N_DFLOAT | CPP_N_LARGE) : 0); break;
+      default:
+       /* Additional two-character suffixes beginning with D are not
+          for decimal float constants.  */
        break;
        break;
-      case 'l': case 'L':
-       if (d > 0)
-         return 0;
-       l++;
+      }
+    }
+
+  /* Recognize a fixed-point suffix.  */
+  switch (s[len-1])
+    {
+    case 'k': case 'K': flags = CPP_N_ACCUM; break;
+    case 'r': case 'R': flags = CPP_N_FRACT; break;
+    default: break;
+    }
+
+  /* Continue processing a fixed-point suffix.  The suffix is case
+     insensitive except for ll or LL.  Order is significant.  */
+  if (flags)
+    {
+      if (len == 1)
+       return flags;
+      len--;
+
+      if (*s == 'u' || *s == 'U')
+       {
+         flags |= CPP_N_UNSIGNED;
+         if (len == 1)
+           return flags;
+         len--;
+         s++;
+        }
+
+      switch (*s)
+      {
+      case 'h': case 'H':
+       if (len == 1)
+         return flags |= CPP_N_SMALL;
+       break;
+      case 'l':
+       if (len == 1)
+         return flags |= CPP_N_MEDIUM;
+       if (len == 2 && s[1] == 'l')
+         return flags |= CPP_N_LARGE;
        break;
        break;
+      case 'L':
+       if (len == 1)
+         return flags |= CPP_N_MEDIUM;
+       if (len == 2 && s[1] == 'L')
+         return flags |= CPP_N_LARGE;
+       break;
+      default:
+       break;
+      }
+      /* Anything left at this point is invalid.  */
+      return 0;
+    }
+
+  /* In any remaining valid suffix, the case and order don't matter.  */
+  while (len--)
+    switch (s[len])
+      {
+      case 'f': case 'F': f++; break;
+      case 'd': case 'D': d++; break;
+      case 'l': case 'L': l++; break;
+      case 'w': case 'W': w++; break;
+      case 'q': case 'Q': q++; break;
       case 'i': case 'I':
       case 'j': case 'J': i++; break;
       case 'i': case 'I':
       case 'j': case 'J': i++; break;
-      case 'd': case 'D': d++; break;
       default:
        return 0;
       }
 
       default:
        return 0;
       }
 
-  if (f + l > 1 || i > 1)
-    return 0;
-
-  /* Allow dd, df, dl suffixes for decimal float constants.  */
-  if (d && ((d + f + l != 2) || i))
+  if (f + d + l + w + q > 1 || i > 1)
     return 0;
 
   return ((i ? CPP_N_IMAGINARY : 0)
          | (f ? CPP_N_SMALL :
     return 0;
 
   return ((i ? CPP_N_IMAGINARY : 0)
          | (f ? CPP_N_SMALL :
-            l ? CPP_N_LARGE : CPP_N_MEDIUM)
-         | (d ? CPP_N_DFLOAT : 0));
+            d ? CPP_N_MEDIUM :
+            l ? CPP_N_LARGE :
+            w ? CPP_N_MD_W :
+            q ? CPP_N_MD_Q : CPP_N_DEFAULT));
 }
 
 /* Subroutine of cpp_classify_number.  S points to an integer suffix
 }
 
 /* Subroutine of cpp_classify_number.  S points to an integer suffix
@@ -224,6 +292,26 @@ cpp_classify_number (cpp_reader *pfile, const cpp_token *token)
        }
     }
 
        }
     }
 
+  /* The suffix may be for decimal fixed-point constants without exponent.  */
+  if (radix != 16 && float_flag == NOT_FLOAT)
+    {
+      result = interpret_float_suffix (str, limit - str);
+      if ((result & CPP_N_FRACT) || (result & CPP_N_ACCUM))
+       {
+         result |= CPP_N_FLOATING;
+         /* We need to restore the radix to 10, if the radix is 8.  */
+         if (radix == 8)
+           radix = 10;
+
+         if (CPP_PEDANTIC (pfile))
+           cpp_error (pfile, CPP_DL_PEDWARN,
+                      "fixed-point constants are a GCC extension");
+         goto syntax_ok;
+       }
+      else
+       result = 0;
+    }
+
   if (float_flag != NOT_FLOAT && radix == 8)
     radix = 10;
 
   if (float_flag != NOT_FLOAT && radix == 8)
     radix = 10;
 
@@ -281,6 +369,13 @@ cpp_classify_number (cpp_reader *pfile, const cpp_token *token)
                   "traditional C rejects the \"%.*s\" suffix",
                   (int) (limit - str), str);
 
                   "traditional C rejects the \"%.*s\" suffix",
                   (int) (limit - str), str);
 
+      /* A suffix for double is a GCC extension via decimal float support.
+        If the suffix also specifies an imaginary value we'll catch that
+        later.  */
+      if ((result == CPP_N_MEDIUM) && CPP_PEDANTIC (pfile))
+       cpp_error (pfile, CPP_DL_PEDWARN,
+                  "suffix for double constant is a GCC extension");
+
       /* Radix must be 10 for decimal floats.  */
       if ((result & CPP_N_DFLOAT) && radix != 10)
         {
       /* Radix must be 10 for decimal floats.  */
       if ((result & CPP_N_DFLOAT) && radix != 10)
         {
@@ -290,6 +385,10 @@ cpp_classify_number (cpp_reader *pfile, const cpp_token *token)
           return CPP_N_INVALID;
         }
 
           return CPP_N_INVALID;
         }
 
+      if ((result & (CPP_N_FRACT | CPP_N_ACCUM)) && CPP_PEDANTIC (pfile))
+       cpp_error (pfile, CPP_DL_PEDWARN,
+                  "fixed-point constants are a GCC extension");
+
       if ((result & CPP_N_DFLOAT) && CPP_PEDANTIC (pfile))
        cpp_error (pfile, CPP_DL_PEDWARN,
                   "decimal float constants are a GCC extension");
       if ((result & CPP_N_DFLOAT) && CPP_PEDANTIC (pfile))
        cpp_error (pfile, CPP_DL_PEDWARN,
                   "decimal float constants are a GCC extension");
@@ -321,14 +420,17 @@ cpp_classify_number (cpp_reader *pfile, const cpp_token *token)
        }
 
       if ((result & CPP_N_WIDTH) == CPP_N_LARGE
        }
 
       if ((result & CPP_N_WIDTH) == CPP_N_LARGE
-         && ! CPP_OPTION (pfile, c99)
          && CPP_OPTION (pfile, warn_long_long))
          && CPP_OPTION (pfile, warn_long_long))
-       cpp_error (pfile, CPP_DL_PEDWARN,
-                  "use of C99 long long integer constant");
+       cpp_error (pfile, 
+                  CPP_OPTION (pfile, c99) ? CPP_DL_WARNING : CPP_DL_PEDWARN,
+                  CPP_OPTION (pfile, cplusplus) 
+                  ? "use of C++0x long long integer constant"
+                  : "use of C99 long long integer constant");
 
       result |= CPP_N_INTEGER;
     }
 
 
       result |= CPP_N_INTEGER;
     }
 
+ syntax_ok:
   if ((result & CPP_N_IMAGINARY) && CPP_PEDANTIC (pfile))
     cpp_error (pfile, CPP_DL_PEDWARN,
               "imaginary constants are a GCC extension");
   if ((result & CPP_N_IMAGINARY) && CPP_PEDANTIC (pfile))
     cpp_error (pfile, CPP_DL_PEDWARN,
               "imaginary constants are a GCC extension");
@@ -437,8 +539,27 @@ cpp_interpret_integer (cpp_reader *pfile, const cpp_token *token,
                    && pfile->state.in_directive)
               && !num_positive (result, precision))
        {
                    && pfile->state.in_directive)
               && !num_positive (result, precision))
        {
+         /* This is for constants within the range of uintmax_t but
+            not that of intmax_t.  For such decimal constants, a
+            diagnostic is required for C99 as the selected type must
+            be signed and not having a type is a constraint violation
+            (DR#298, TC3), so this must be a pedwarn.  For C90,
+            unsigned long is specified to be used for a constant that
+            does not fit in signed long; if uintmax_t has the same
+            range as unsigned long this means only a warning is
+            appropriate here.  C90 permits the preprocessor to use a
+            wider range than unsigned long in the compiler, so if
+            uintmax_t is wider than unsigned long no diagnostic is
+            required for such constants in preprocessor #if
+            expressions and the compiler will pedwarn for such
+            constants outside the range of unsigned long that reach
+            the compiler so a diagnostic is not required there
+            either; thus, pedwarn for C99 but use a plain warning for
+            C90.  */
          if (base == 10)
          if (base == 10)
-           cpp_error (pfile, CPP_DL_WARNING,
+           cpp_error (pfile, (CPP_OPTION (pfile, c99)
+                              ? CPP_DL_PEDWARN
+                              : CPP_DL_WARNING),
                       "integer constant is so large that it is unsigned");
          result.unsignedp = true;
        }
                       "integer constant is so large that it is unsigned");
          result.unsignedp = true;
        }
@@ -531,7 +652,7 @@ parse_defined (cpp_reader *pfile)
 
   if (token->type == CPP_NAME)
     {
 
   if (token->type == CPP_NAME)
     {
-      node = token->val.node;
+      node = token->val.node.node;
       if (paren && cpp_get_token (pfile)->type != CPP_CLOSE_PAREN)
        {
          cpp_error (pfile, CPP_DL_ERROR, "missing ')' after \"defined\"");
       if (paren && cpp_get_token (pfile)->type != CPP_CLOSE_PAREN)
        {
          cpp_error (pfile, CPP_DL_ERROR, "missing ')' after \"defined\"");
@@ -562,6 +683,20 @@ parse_defined (cpp_reader *pfile)
                   "this use of \"defined\" may not be portable");
 
       _cpp_mark_macro_used (node);
                   "this use of \"defined\" may not be portable");
 
       _cpp_mark_macro_used (node);
+      if (!(node->flags & NODE_USED))
+       {
+         node->flags |= NODE_USED;
+         if (node->type == NT_MACRO)
+           {
+             if (pfile->cb.used_define)
+               pfile->cb.used_define (pfile, pfile->directive_line, node);
+           }
+         else
+           {
+             if (pfile->cb.used_undef)
+               pfile->cb.used_undef (pfile, pfile->directive_line, node);
+           }
+       }
 
       /* A possible controlling macro of the form #if !defined ().
         _cpp_parse_expr checks there was no other junk on the line.  */
 
       /* A possible controlling macro of the form #if !defined ().
         _cpp_parse_expr checks there was no other junk on the line.  */
@@ -616,6 +751,8 @@ eval_token (cpp_reader *pfile, const cpp_token *token)
 
     case CPP_WCHAR:
     case CPP_CHAR:
 
     case CPP_WCHAR:
     case CPP_CHAR:
+    case CPP_CHAR16:
+    case CPP_CHAR32:
       {
        cppchar_t cc = cpp_interpret_charconst (pfile, token,
                                                &temp, &unsignedp);
       {
        cppchar_t cc = cpp_interpret_charconst (pfile, token,
                                                &temp, &unsignedp);
@@ -635,14 +772,14 @@ eval_token (cpp_reader *pfile, const cpp_token *token)
       break;
 
     case CPP_NAME:
       break;
 
     case CPP_NAME:
-      if (token->val.node == pfile->spec_nodes.n_defined)
+      if (token->val.node.node == pfile->spec_nodes.n_defined)
        return parse_defined (pfile);
       else if (CPP_OPTION (pfile, cplusplus)
        return parse_defined (pfile);
       else if (CPP_OPTION (pfile, cplusplus)
-              && (token->val.node == pfile->spec_nodes.n_true
-                  || token->val.node == pfile->spec_nodes.n_false))
+              && (token->val.node.node == pfile->spec_nodes.n_true
+                  || token->val.node.node == pfile->spec_nodes.n_false))
        {
          result.high = 0;
        {
          result.high = 0;
-         result.low = (token->val.node == pfile->spec_nodes.n_true);
+         result.low = (token->val.node.node == pfile->spec_nodes.n_true);
        }
       else
        {
        }
       else
        {
@@ -650,14 +787,29 @@ eval_token (cpp_reader *pfile, const cpp_token *token)
          result.low = 0;
          if (CPP_OPTION (pfile, warn_undef) && !pfile->state.skip_eval)
            cpp_error (pfile, CPP_DL_WARNING, "\"%s\" is not defined",
          result.low = 0;
          if (CPP_OPTION (pfile, warn_undef) && !pfile->state.skip_eval)
            cpp_error (pfile, CPP_DL_WARNING, "\"%s\" is not defined",
-                      NODE_NAME (token->val.node));
+                      NODE_NAME (token->val.node.node));
        }
       break;
 
        }
       break;
 
-    default: /* CPP_HASH */
+    case CPP_HASH:
+      if (!pfile->state.skipping)
+       {
+         /* A pedantic warning takes precedence over a deprecated
+            warning here.  */
+         if (CPP_PEDANTIC (pfile))
+           cpp_error (pfile, CPP_DL_PEDWARN,
+                      "assertions are a GCC extension");
+         else if (CPP_OPTION (pfile, warn_deprecated))
+           cpp_error (pfile, CPP_DL_WARNING,
+                      "assertions are a deprecated extension");
+       }
       _cpp_test_assertion (pfile, &temp);
       result.high = 0;
       result.low = temp;
       _cpp_test_assertion (pfile, &temp);
       result.high = 0;
       result.low = temp;
+      break;
+
+    default:
+      abort ();
     }
 
   result.unsignedp = !!unsignedp;
     }
 
   result.unsignedp = !!unsignedp;
@@ -718,9 +870,11 @@ static const struct cpp_operator
   /* COMPL */          {16, NO_L_OPERAND},
   /* AND_AND */                {6, LEFT_ASSOC},
   /* OR_OR */          {5, LEFT_ASSOC},
   /* COMPL */          {16, NO_L_OPERAND},
   /* AND_AND */                {6, LEFT_ASSOC},
   /* OR_OR */          {5, LEFT_ASSOC},
-  /* QUERY */          {3, 0},
+  /* Note that QUERY, COLON, and COMMA must have the same precedence.
+     However, there are some special cases for these in reduce().  */
+  /* QUERY */          {4, 0},
   /* COLON */          {4, LEFT_ASSOC | CHECK_PROMOTION},
   /* COLON */          {4, LEFT_ASSOC | CHECK_PROMOTION},
-  /* COMMA */          {2, LEFT_ASSOC},
+  /* COMMA */          {4, LEFT_ASSOC},
   /* OPEN_PAREN */     {1, NO_L_OPERAND},
   /* CLOSE_PAREN */    {0, 0},
   /* EOF */            {0, 0},
   /* OPEN_PAREN */     {1, NO_L_OPERAND},
   /* CLOSE_PAREN */    {0, 0},
   /* EOF */            {0, 0},
@@ -744,7 +898,7 @@ static const struct cpp_operator
    stored in the 'value' field of the stack element of the operator
    that precedes it.  */
 bool
    stored in the 'value' field of the stack element of the operator
    that precedes it.  */
 bool
-_cpp_parse_expr (cpp_reader *pfile)
+_cpp_parse_expr (cpp_reader *pfile, bool is_if)
 {
   struct op *top = pfile->op_stack;
   unsigned int lex_count;
 {
   struct op *top = pfile->op_stack;
   unsigned int lex_count;
@@ -767,6 +921,7 @@ _cpp_parse_expr (cpp_reader *pfile)
       lex_count++;
       op.token = cpp_get_token (pfile);
       op.op = op.token->type;
       lex_count++;
       op.token = cpp_get_token (pfile);
       op.op = op.token->type;
+      op.loc = op.token->src_loc;
 
       switch (op.op)
        {
 
       switch (op.op)
        {
@@ -774,6 +929,8 @@ _cpp_parse_expr (cpp_reader *pfile)
        case CPP_NUMBER:
        case CPP_CHAR:
        case CPP_WCHAR:
        case CPP_NUMBER:
        case CPP_CHAR:
        case CPP_WCHAR:
+       case CPP_CHAR16:
+       case CPP_CHAR32:
        case CPP_NAME:
        case CPP_HASH:
          if (!want_value)
        case CPP_NAME:
        case CPP_HASH:
          if (!want_value)
@@ -817,7 +974,7 @@ _cpp_parse_expr (cpp_reader *pfile)
            SYNTAX_ERROR ("missing expression between '(' and ')'");
 
          if (op.op == CPP_EOF && top->op == CPP_EOF)
            SYNTAX_ERROR ("missing expression between '(' and ')'");
 
          if (op.op == CPP_EOF && top->op == CPP_EOF)
-           SYNTAX_ERROR ("#if with no expression");
+           SYNTAX_ERROR2 ("%s with no expression", is_if ? "#if" : "#elif");
 
          if (top->op != CPP_EOF && top->op != CPP_OPEN_PAREN)
            SYNTAX_ERROR2 ("operator '%s' has no right operand",
 
          if (top->op != CPP_EOF && top->op != CPP_OPEN_PAREN)
            SYNTAX_ERROR2 ("operator '%s' has no right operand",
@@ -868,6 +1025,7 @@ _cpp_parse_expr (cpp_reader *pfile)
 
       top->op = op.op;
       top->token = op.token;
 
       top->op = op.op;
       top->token = op.token;
+      top->loc = op.token->src_loc;
     }
 
   /* The controlling macro expression is only valid if we called lex 3
     }
 
   /* The controlling macro expression is only valid if we called lex 3
@@ -878,7 +1036,8 @@ _cpp_parse_expr (cpp_reader *pfile)
 
   if (top != pfile->op_stack)
     {
 
   if (top != pfile->op_stack)
     {
-      cpp_error (pfile, CPP_DL_ICE, "unbalanced stack in #if");
+      cpp_error (pfile, CPP_DL_ICE, "unbalanced stack in %s",
+                is_if ? "#if" : "#elif");
     syntax_error:
       return false;  /* Return false on syntax error.  */
     }
     syntax_error:
       return false;  /* Return false on syntax error.  */
     }
@@ -920,6 +1079,7 @@ reduce (cpp_reader *pfile, struct op *top, enum cpp_ttype op)
        case CPP_NOT:
        case CPP_COMPL:
          top[-1].value = num_unary_op (pfile, top->value, top->op);
        case CPP_NOT:
        case CPP_COMPL:
          top[-1].value = num_unary_op (pfile, top->value, top->op);
+         top[-1].loc = top->loc;
          break;
 
        case CPP_PLUS:
          break;
 
        case CPP_PLUS:
@@ -929,6 +1089,7 @@ reduce (cpp_reader *pfile, struct op *top, enum cpp_ttype op)
        case CPP_COMMA:
          top[-1].value = num_binary_op (pfile, top[-1].value,
                                         top->value, top->op);
        case CPP_COMMA:
          top[-1].value = num_binary_op (pfile, top[-1].value,
                                         top->value, top->op);
+         top[-1].loc = top->loc;
          break;
 
        case CPP_GREATER:
          break;
 
        case CPP_GREATER:
@@ -937,12 +1098,14 @@ reduce (cpp_reader *pfile, struct op *top, enum cpp_ttype op)
        case CPP_LESS_EQ:
          top[-1].value
            = num_inequality_op (pfile, top[-1].value, top->value, top->op);
        case CPP_LESS_EQ:
          top[-1].value
            = num_inequality_op (pfile, top[-1].value, top->value, top->op);
+         top[-1].loc = top->loc;
          break;
 
        case CPP_EQ_EQ:
        case CPP_NOT_EQ:
          top[-1].value
            = num_equality_op (pfile, top[-1].value, top->value, top->op);
          break;
 
        case CPP_EQ_EQ:
        case CPP_NOT_EQ:
          top[-1].value
            = num_equality_op (pfile, top[-1].value, top->value, top->op);
+         top[-1].loc = top->loc;
          break;
 
        case CPP_AND:
          break;
 
        case CPP_AND:
@@ -950,16 +1113,19 @@ reduce (cpp_reader *pfile, struct op *top, enum cpp_ttype op)
        case CPP_XOR:
          top[-1].value
            = num_bitwise_op (pfile, top[-1].value, top->value, top->op);
        case CPP_XOR:
          top[-1].value
            = num_bitwise_op (pfile, top[-1].value, top->value, top->op);
+         top[-1].loc = top->loc;
          break;
 
        case CPP_MULT:
          top[-1].value = num_mul (pfile, top[-1].value, top->value);
          break;
 
        case CPP_MULT:
          top[-1].value = num_mul (pfile, top[-1].value, top->value);
+         top[-1].loc = top->loc;
          break;
 
        case CPP_DIV:
        case CPP_MOD:
          top[-1].value = num_div_op (pfile, top[-1].value,
          break;
 
        case CPP_DIV:
        case CPP_MOD:
          top[-1].value = num_div_op (pfile, top[-1].value,
-                                     top->value, top->op);
+                                     top->value, top->op, top->loc);
+         top[-1].loc = top->loc;
          break;
 
        case CPP_OR_OR:
          break;
 
        case CPP_OR_OR:
@@ -971,6 +1137,7 @@ reduce (cpp_reader *pfile, struct op *top, enum cpp_ttype op)
          top->value.high = 0;
          top->value.unsignedp = false;
          top->value.overflow = false;
          top->value.high = 0;
          top->value.unsignedp = false;
          top->value.overflow = false;
+         top->loc = top[1].loc;
          continue;
 
        case CPP_AND_AND:
          continue;
 
        case CPP_AND_AND:
@@ -982,16 +1149,20 @@ reduce (cpp_reader *pfile, struct op *top, enum cpp_ttype op)
          top->value.high = 0;
          top->value.unsignedp = false;
          top->value.overflow = false;
          top->value.high = 0;
          top->value.unsignedp = false;
          top->value.overflow = false;
+         top->loc = top[1].loc;
          continue;
 
        case CPP_OPEN_PAREN:
          if (op != CPP_CLOSE_PAREN)
            {
          continue;
 
        case CPP_OPEN_PAREN:
          if (op != CPP_CLOSE_PAREN)
            {
-             cpp_error (pfile, CPP_DL_ERROR, "missing ')' in expression");
+             cpp_error_with_line (pfile, CPP_DL_ERROR, 
+                                  top->token->src_loc,
+                                  0, "missing ')' in expression");
              return 0;
            }
          top--;
          top->value = top[1].value;
              return 0;
            }
          top--;
          top->value = top[1].value;
+         top->loc = top[1].loc;
          return top;
 
        case CPP_COLON:
          return top;
 
        case CPP_COLON:
@@ -1000,14 +1171,21 @@ reduce (cpp_reader *pfile, struct op *top, enum cpp_ttype op)
            {
              pfile->state.skip_eval--;
              top->value = top[1].value;
            {
              pfile->state.skip_eval--;
              top->value = top[1].value;
+             top->loc = top[1].loc;
            }
          else
            }
          else
-           top->value = top[2].value;
+           {
+             top->value = top[2].value;
+             top->loc = top[2].loc;
+           }
          top->value.unsignedp = (top[1].value.unsignedp
                                  || top[2].value.unsignedp);
          continue;
 
        case CPP_QUERY:
          top->value.unsignedp = (top[1].value.unsignedp
                                  || top[2].value.unsignedp);
          continue;
 
        case CPP_QUERY:
+         /* COMMA and COLON should not reduce a QUERY operator.  */
+         if (op == CPP_COMMA || op == CPP_COLON)
+           return top;
          cpp_error (pfile, CPP_DL_ERROR, "'?' without following ':'");
          return 0;
 
          cpp_error (pfile, CPP_DL_ERROR, "'?' without following ':'");
          return 0;
 
@@ -1054,12 +1232,12 @@ check_promotion (cpp_reader *pfile, const struct op *op)
   if (op->value.unsignedp)
     {
       if (!num_positive (op[-1].value, CPP_OPTION (pfile, precision)))
   if (op->value.unsignedp)
     {
       if (!num_positive (op[-1].value, CPP_OPTION (pfile, precision)))
-       cpp_error (pfile, CPP_DL_WARNING,
-                  "the left operand of \"%s\" changes sign when promoted",
-                  cpp_token_as_text (pfile, op->token));
+       cpp_error_with_line (pfile, CPP_DL_WARNING, op[-1].loc, 0,
+                            "the left operand of \"%s\" changes sign when promoted",
+                            cpp_token_as_text (pfile, op->token));
     }
   else if (!num_positive (op->value, CPP_OPTION (pfile, precision)))
     }
   else if (!num_positive (op->value, CPP_OPTION (pfile, precision)))
-    cpp_error (pfile, CPP_DL_WARNING,
+    cpp_error_with_line (pfile, CPP_DL_WARNING, op->loc, 0,
               "the right operand of \"%s\" changes sign when promoted",
               cpp_token_as_text (pfile, op->token));
 }
               "the right operand of \"%s\" changes sign when promoted",
               cpp_token_as_text (pfile, op->token));
 }
@@ -1491,10 +1669,13 @@ num_mul (cpp_reader *pfile, cpp_num lhs, cpp_num rhs)
   return result;
 }
 
   return result;
 }
 
-/* Divide two preprocessing numbers, returning the answer or the
-   remainder depending upon OP.  */
+/* Divide two preprocessing numbers, LHS and RHS, returning the answer
+   or the remainder depending upon OP. LOCATION is the source location
+   of this operator (for diagnostics).  */
+
 static cpp_num
 static cpp_num
-num_div_op (cpp_reader *pfile, cpp_num lhs, cpp_num rhs, enum cpp_ttype op)
+num_div_op (cpp_reader *pfile, cpp_num lhs, cpp_num rhs, enum cpp_ttype op,
+           source_location location)
 {
   cpp_num result, sub;
   cpp_num_part mask;
 {
   cpp_num result, sub;
   cpp_num_part mask;
@@ -1534,7 +1715,8 @@ num_div_op (cpp_reader *pfile, cpp_num lhs, cpp_num rhs, enum cpp_ttype op)
   else
     {
       if (!pfile->state.skip_eval)
   else
     {
       if (!pfile->state.skip_eval)
-       cpp_error (pfile, CPP_DL_ERROR, "division by zero in #if");
+       cpp_error_with_line (pfile, CPP_DL_ERROR, location, 0,
+                            "division by zero in #if");
       return lhs;
     }
 
       return lhs;
     }