c/c++: new warning: -Wxor-used-as-pow [PR90885]

Message ID 20220812013812.3183820-1-dmalcolm@redhat.com
State New
Headers
Series c/c++: new warning: -Wxor-used-as-pow [PR90885] |

Commit Message

David Malcolm Aug. 12, 2022, 1:38 a.m. UTC
  PR c/90885 notes various places in real-world code where people have
written C/C++ code that uses ^ (exclusive or) where presumbably they
meant exponentiation.

For example
  https://codesearch.isocpp.org/cgi-bin/cgi_ppsearch?q=2%5E32&search=Search
currently finds 11 places using "2^32", and all of them appear to be
places where the user means 2 to the power of 32, rather than 2
exclusive-orred with 32 (which is 34).

This patch adds a new -Wxor-used-as-pow warning to the C and C++
frontends to complain about ^ when the left-hand side is the decimal
constant 2 or the decimal constant 10.

This is the same name as the corresponding clang warning:
  https://clang.llvm.org/docs/DiagnosticsReference.html#wxor-used-as-pow

As per the clang warning, the warning suggests converting the left-hand
side to a hexadecimal constant if you really mean xor, which suppresses
the warning (and this patch implements a fix-it hint for that, whereas
the clang implementation only has a fix-it hint for the initial
suggestion of exponentiation).

I initially tried implementing this without checking for decimals, but
this version had lots of false positives.  Checking for decimals
requires extending the lexer to capture whether or not a CPP_NUMBER
token was decimal.  I added a new DECIMAL_INT flag to cpplib.h for this.
Unfortunately, c_token and cp_tokens both have only an unsigned char for
their flags (as captured by c_lex_with_flags), whereas this would add
the 12th flag to cpp_tokens.  Of the first 8 flags, all but BOL are used
in the C or C++ frontends, but BOL is not, so I moved that to a higher
position, using its old value for the new DECIMAL_INT flag, so that it
is representable within an unsigned char.

Example output:

demo.c:5:13: warning: result of '2^8' is 10; did you mean '1 << 8' (256)? [-Wxor-used-as-pow]
    5 | int t2_8 = 2^8;
      |             ^
      |            --
      |            1<<
demo.c:5:12: note: you can silence this warning by using a hexadecimal constant (0x2 rather than 2)
    5 | int t2_8 = 2^8;
      |            ^
      |            0x2
demo.c:21:15: warning: result of '10^6' is 12; did you mean '1e6'? [-Wxor-used-as-pow]
   21 | int t10_6 = 10^6;
      |               ^
      |             ---
      |             1e
demo.c:21:13: note: you can silence this warning by using a hexadecimal constant (0xa rather than 10)
   21 | int t10_6 = 10^6;
      |             ^~
      |             0xa

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

Thanks
Dave


gcc/c-family/ChangeLog:
	PR c/90885
	* c-common.h (check_for_xor_used_as_pow): New decl.
	* c-lex.cc (c_lex_with_flags): Add DECIMAL_INT to flags as appropriate.
	* c-warn.cc (check_for_xor_used_as_pow): New.
	* c.opt (Wxor-used-as-pow): New.

gcc/c/ChangeLog:
	PR c/90885
	* c-parser.cc (c_parser_string_literal): Clear ret.m_decimal.
	(c_parser_expr_no_commas): Likewise.
	(c_parser_conditional_expression): Likewise.
	(c_parser_binary_expression): Clear m_decimal when popping the
	stack.
	(c_parser_unary_expression): Clear ret.m_decimal.
	(c_parser_has_attribute_expression): Likewise for result.
	(c_parser_predefined_identifier): Likewise for expr.
	(c_parser_postfix_expression): Likewise for expr.
	Set expr.m_decimal when handling a CPP_NUMBER that was a decimal
	token.
	* c-tree.h (c_expr::m_decimal): New bitfield.
	* c-typeck.cc (parser_build_binary_op): Clear result.m_decimal.
	(parser_build_binary_op): Call check_for_xor_used_as_pow.

gcc/cp/ChangeLog:
	PR c/90885
	* cp-tree.h (class cp_expr): Add bitfield m_decimal.  Clear it in
	existing ctors.  Add ctor that allows specifying its value.
	(cp_expr::decimal_p): New accessor.
	* parser.cc (cp_parser_expression_stack_entry::flags): New field.
	(cp_parser_primary_expression): Set m_decimal of cp_expr when
	handling numbers.
	(cp_parser_binary_expression): Extract flags from token when
	populating stack.  Call check_for_xor_used_as_pow.

gcc/ChangeLog:
	PR c/90885
	* doc/invoke.texi (Warning Options): Add -Wxor-used-as-pow.

gcc/testsuite/ChangeLog:
	PR c/90885
	* c-c++-common/Wxor-used-as-pow-1.c: New test.
	* c-c++-common/Wxor-used-as-pow-fixits.c: New test.
	* g++.dg/parse/expr3.C: Convert 2 to 0x2 to suppress
	-Wxor-used-as-pow.
	* g++.dg/warn/Wparentheses-10.C: Likewise.
	* g++.dg/warn/Wparentheses-18.C: Likewise.
	* g++.dg/warn/Wparentheses-19.C: Likewise.
	* g++.dg/warn/Wparentheses-9.C: Likewise.
	* g++.dg/warn/Wxor-used-as-pow-named-op.C: New test.
	* gcc.dg/Wparentheses-6.c: Convert 2 to 0x2 to suppress
	-Wxor-used-as-pow.
	* gcc.dg/Wparentheses-7.c: Likewise.
	* gcc.dg/precedence-1.c: Likewise.

libcpp/ChangeLog:
	PR c/90885
	* include/cpplib.h (BOL): Move macro to 1 << 12 since it is
	not used by C/C++'s unsigned char token flags.
	(DECIMAL_INT): New, using 1 << 6, so that it is visible as
	part of C/C++'s 8 bits of token flags.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/c-family/c-common.h                       |  4 +
 gcc/c-family/c-lex.cc                         |  6 +-
 gcc/c-family/c-warn.cc                        | 94 +++++++++++++++++++
 gcc/c-family/c.opt                            |  4 +
 gcc/c/c-parser.cc                             |  9 ++
 gcc/c/c-tree.h                                |  3 +
 gcc/c/c-typeck.cc                             |  9 ++
 gcc/cp/cp-tree.h                              | 19 +++-
 gcc/cp/parser.cc                              | 17 +++-
 gcc/doc/invoke.texi                           | 15 +++
 .../c-c++-common/Wxor-used-as-pow-1.c         | 57 +++++++++++
 .../c-c++-common/Wxor-used-as-pow-fixits.c    | 34 +++++++
 gcc/testsuite/g++.dg/parse/expr3.C            |  2 +-
 gcc/testsuite/g++.dg/warn/Wparentheses-10.C   | 14 +--
 gcc/testsuite/g++.dg/warn/Wparentheses-18.C   |  4 +-
 gcc/testsuite/g++.dg/warn/Wparentheses-19.C   | 12 +--
 gcc/testsuite/g++.dg/warn/Wparentheses-9.C    |  4 +-
 .../g++.dg/warn/Wxor-used-as-pow-named-op.C   |  8 ++
 gcc/testsuite/gcc.dg/Wparentheses-6.c         |  4 +-
 gcc/testsuite/gcc.dg/Wparentheses-7.c         | 12 +--
 gcc/testsuite/gcc.dg/precedence-1.c           |  4 +-
 libcpp/include/cpplib.h                       |  3 +-
 22 files changed, 304 insertions(+), 34 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c
 create mode 100644 gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c
 create mode 100644 gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C
  

Comments

Jason Merrill Aug. 30, 2022, 8:18 p.m. UTC | #1
On 8/11/22 21:38, David Malcolm via Gcc-patches wrote:
> PR c/90885 notes various places in real-world code where people have
> written C/C++ code that uses ^ (exclusive or) where presumbably they
> meant exponentiation.
> 
> For example
>    https://codesearch.isocpp.org/cgi-bin/cgi_ppsearch?q=2%5E32&search=Search
> currently finds 11 places using "2^32", and all of them appear to be
> places where the user means 2 to the power of 32, rather than 2
> exclusive-orred with 32 (which is 34).
> 
> This patch adds a new -Wxor-used-as-pow warning to the C and C++
> frontends to complain about ^ when the left-hand side is the decimal
> constant 2 or the decimal constant 10.
> 
> This is the same name as the corresponding clang warning:
>    https://clang.llvm.org/docs/DiagnosticsReference.html#wxor-used-as-pow
> 
> As per the clang warning, the warning suggests converting the left-hand
> side to a hexadecimal constant if you really mean xor, which suppresses
> the warning (and this patch implements a fix-it hint for that, whereas
> the clang implementation only has a fix-it hint for the initial
> suggestion of exponentiation).
> 
> I initially tried implementing this without checking for decimals, but
> this version had lots of false positives.  Checking for decimals
> requires extending the lexer to capture whether or not a CPP_NUMBER
> token was decimal.  I added a new DECIMAL_INT flag to cpplib.h for this.
> Unfortunately, c_token and cp_tokens both have only an unsigned char for
> their flags (as captured by c_lex_with_flags), whereas this would add
> the 12th flag to cpp_tokens.  Of the first 8 flags, all but BOL are used
> in the C or C++ frontends, but BOL is not, so I moved that to a higher
> position, using its old value for the new DECIMAL_INT flag, so that it
> is representable within an unsigned char.
> 
> Example output:
> 
> demo.c:5:13: warning: result of '2^8' is 10; did you mean '1 << 8' (256)? [-Wxor-used-as-pow]
>      5 | int t2_8 = 2^8;
>        |             ^
>        |            --
>        |            1<<
> demo.c:5:12: note: you can silence this warning by using a hexadecimal constant (0x2 rather than 2)
>      5 | int t2_8 = 2^8;
>        |            ^
>        |            0x2
> demo.c:21:15: warning: result of '10^6' is 12; did you mean '1e6'? [-Wxor-used-as-pow]
>     21 | int t10_6 = 10^6;
>        |               ^
>        |             ---
>        |             1e
> demo.c:21:13: note: you can silence this warning by using a hexadecimal constant (0xa rather than 10)
>     21 | int t10_6 = 10^6;
>        |             ^~
>        |             0xa
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> 
> OK for trunk?

Looks good to me, but a C maintainer should sign off on the C front-end 
changes.

> Thanks
> Dave
> 
> 
> gcc/c-family/ChangeLog:
> 	PR c/90885
> 	* c-common.h (check_for_xor_used_as_pow): New decl.
> 	* c-lex.cc (c_lex_with_flags): Add DECIMAL_INT to flags as appropriate.
> 	* c-warn.cc (check_for_xor_used_as_pow): New.
> 	* c.opt (Wxor-used-as-pow): New.
> 
> gcc/c/ChangeLog:
> 	PR c/90885
> 	* c-parser.cc (c_parser_string_literal): Clear ret.m_decimal.
> 	(c_parser_expr_no_commas): Likewise.
> 	(c_parser_conditional_expression): Likewise.
> 	(c_parser_binary_expression): Clear m_decimal when popping the
> 	stack.
> 	(c_parser_unary_expression): Clear ret.m_decimal.
> 	(c_parser_has_attribute_expression): Likewise for result.
> 	(c_parser_predefined_identifier): Likewise for expr.
> 	(c_parser_postfix_expression): Likewise for expr.
> 	Set expr.m_decimal when handling a CPP_NUMBER that was a decimal
> 	token.
> 	* c-tree.h (c_expr::m_decimal): New bitfield.
> 	* c-typeck.cc (parser_build_binary_op): Clear result.m_decimal.
> 	(parser_build_binary_op): Call check_for_xor_used_as_pow.
> 
> gcc/cp/ChangeLog:
> 	PR c/90885
> 	* cp-tree.h (class cp_expr): Add bitfield m_decimal.  Clear it in
> 	existing ctors.  Add ctor that allows specifying its value.
> 	(cp_expr::decimal_p): New accessor.
> 	* parser.cc (cp_parser_expression_stack_entry::flags): New field.
> 	(cp_parser_primary_expression): Set m_decimal of cp_expr when
> 	handling numbers.
> 	(cp_parser_binary_expression): Extract flags from token when
> 	populating stack.  Call check_for_xor_used_as_pow.
> 
> gcc/ChangeLog:
> 	PR c/90885
> 	* doc/invoke.texi (Warning Options): Add -Wxor-used-as-pow.
> 
> gcc/testsuite/ChangeLog:
> 	PR c/90885
> 	* c-c++-common/Wxor-used-as-pow-1.c: New test.
> 	* c-c++-common/Wxor-used-as-pow-fixits.c: New test.
> 	* g++.dg/parse/expr3.C: Convert 2 to 0x2 to suppress
> 	-Wxor-used-as-pow.
> 	* g++.dg/warn/Wparentheses-10.C: Likewise.
> 	* g++.dg/warn/Wparentheses-18.C: Likewise.
> 	* g++.dg/warn/Wparentheses-19.C: Likewise.
> 	* g++.dg/warn/Wparentheses-9.C: Likewise.
> 	* g++.dg/warn/Wxor-used-as-pow-named-op.C: New test.
> 	* gcc.dg/Wparentheses-6.c: Convert 2 to 0x2 to suppress
> 	-Wxor-used-as-pow.
> 	* gcc.dg/Wparentheses-7.c: Likewise.
> 	* gcc.dg/precedence-1.c: Likewise.
> 
> libcpp/ChangeLog:
> 	PR c/90885
> 	* include/cpplib.h (BOL): Move macro to 1 << 12 since it is
> 	not used by C/C++'s unsigned char token flags.
> 	(DECIMAL_INT): New, using 1 << 6, so that it is visible as
> 	part of C/C++'s 8 bits of token flags.
> 
> Signed-off-by: David Malcolm <dmalcolm@redhat.com>
> ---
>   gcc/c-family/c-common.h                       |  4 +
>   gcc/c-family/c-lex.cc                         |  6 +-
>   gcc/c-family/c-warn.cc                        | 94 +++++++++++++++++++
>   gcc/c-family/c.opt                            |  4 +
>   gcc/c/c-parser.cc                             |  9 ++
>   gcc/c/c-tree.h                                |  3 +
>   gcc/c/c-typeck.cc                             |  9 ++
>   gcc/cp/cp-tree.h                              | 19 +++-
>   gcc/cp/parser.cc                              | 17 +++-
>   gcc/doc/invoke.texi                           | 15 +++
>   .../c-c++-common/Wxor-used-as-pow-1.c         | 57 +++++++++++
>   .../c-c++-common/Wxor-used-as-pow-fixits.c    | 34 +++++++
>   gcc/testsuite/g++.dg/parse/expr3.C            |  2 +-
>   gcc/testsuite/g++.dg/warn/Wparentheses-10.C   | 14 +--
>   gcc/testsuite/g++.dg/warn/Wparentheses-18.C   |  4 +-
>   gcc/testsuite/g++.dg/warn/Wparentheses-19.C   | 12 +--
>   gcc/testsuite/g++.dg/warn/Wparentheses-9.C    |  4 +-
>   .../g++.dg/warn/Wxor-used-as-pow-named-op.C   |  8 ++
>   gcc/testsuite/gcc.dg/Wparentheses-6.c         |  4 +-
>   gcc/testsuite/gcc.dg/Wparentheses-7.c         | 12 +--
>   gcc/testsuite/gcc.dg/precedence-1.c           |  4 +-
>   libcpp/include/cpplib.h                       |  3 +-
>   22 files changed, 304 insertions(+), 34 deletions(-)
>   create mode 100644 gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c
>   create mode 100644 gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c
>   create mode 100644 gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C
> 
> diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
> index c06769b6f0b..78ce94c6591 100644
> --- a/gcc/c-family/c-common.h
> +++ b/gcc/c-family/c-common.h
> @@ -1475,6 +1475,10 @@ extern tree do_warn_duplicated_branches_r (tree *, int *, void *);
>   extern void warn_for_multistatement_macros (location_t, location_t,
>   					    location_t, enum rid);
>   
> +extern void check_for_xor_used_as_pow (location_t lhs_loc, tree lhs_val,
> +				       location_t operator_loc,
> +				       tree rhs_val);
> +
>   /* In c-attribs.cc.  */
>   extern bool attribute_takes_identifier_p (const_tree);
>   extern tree handle_deprecated_attribute (tree *, tree, tree, int, bool *);
> diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
> index 8bfa4f4024f..1eda6f1fccc 100644
> --- a/gcc/c-family/c-lex.cc
> +++ b/gcc/c-family/c-lex.cc
> @@ -509,7 +509,11 @@ c_lex_with_flags (tree *value, location_t *loc, unsigned char *cpp_flags,
>   	    /* C++ uses '0' to mark virtual functions as pure.
>   	       Set PURE_ZERO to pass this information to the C++ parser.  */
>   	    if (tok->val.str.len == 1 && *tok->val.str.text == '0')
> -	      add_flags = PURE_ZERO;
> +	      add_flags = PURE_ZERO | DECIMAL_INT;
> +	    else if ((flags & CPP_N_INTEGER) && (flags & CPP_N_DECIMAL))
> +	      /* -Wxor-used-as-pow is only active for LHS of ^ expressed
> +		 as a decimal integer. */
> +	      add_flags = DECIMAL_INT;
>   	    *value = interpret_integer (tok, flags, &overflow);
>   	    break;
>   
> diff --git a/gcc/c-family/c-warn.cc b/gcc/c-family/c-warn.cc
> index ea7335f3edf..ed79cc3ca40 100644
> --- a/gcc/c-family/c-warn.cc
> +++ b/gcc/c-family/c-warn.cc
> @@ -3799,3 +3799,97 @@ do_warn_array_compare (location_t location, tree_code code, tree op0, tree op1)
>   		op0, op_symbol_code (code), op1);
>       }
>   }
> +
> +/* Given LHS_VAL ^ RHS_VAL, where LHS_LOC is the location of the LHS and
> +   OPERATOR_LOC is the location of the ^, complain with -Wxor-used-as-pow
> +   if it looks like the user meant exponentiation rather than xor.  */
> +
> +void
> +check_for_xor_used_as_pow (location_t lhs_loc, tree lhs_val,
> +			   location_t operator_loc,
> +			   tree rhs_val)
> +{
> +  /* Only complain if both args are non-negative integer constants.  */
> +  if (!(TREE_CODE (lhs_val) == INTEGER_CST
> +	&& tree_int_cst_sgn (lhs_val) >= 0))
> +    return;
> +  if (!(TREE_CODE (rhs_val) == INTEGER_CST
> +	&& tree_int_cst_sgn (rhs_val) >= 0))
> +    return;
> +
> +  /* Only complain if the LHS is 2 or 10.  */
> +  unsigned HOST_WIDE_INT lhs_uhwi = tree_to_uhwi (lhs_val);
> +  if (lhs_uhwi != 2 && lhs_uhwi != 10)
> +    return;
> +
> +  unsigned HOST_WIDE_INT rhs_uhwi = tree_to_uhwi (rhs_val);
> +  unsigned HOST_WIDE_INT xor_result = lhs_uhwi ^ rhs_uhwi;
> +  binary_op_rich_location loc (operator_loc,
> +			       lhs_val, rhs_val, false);
> +
> +  /* If we issue fix-it hints with the warning then we will also issue a
> +     note suggesting how to suppress the warning with a different change.
> +     These proposed changes are incompatible.  */
> +  loc.fixits_cannot_be_auto_applied ();
> +
> +  auto_diagnostic_group d;
> +  bool warned = false;
> +  if (lhs_uhwi == 2)
> +    {
> +      /* Would exponentiation fit in int, in long long, or not at all?  */
> +      if (rhs_uhwi < (INT_TYPE_SIZE - 1))
> +	{
> +	  unsigned HOST_WIDE_INT suggested_result = 1 << rhs_uhwi;
> +	  loc.add_fixit_replace (lhs_loc, "1");
> +	  loc.add_fixit_replace (operator_loc, "<<");
> +	  warned = warning_at (&loc, OPT_Wxor_used_as_pow,
> +			       "result of %<%wu^%wu%> is %wu;"
> +			       " did you mean %<1 << %wu%> (%wu)?",
> +			       lhs_uhwi, rhs_uhwi, xor_result,
> +			       rhs_uhwi, suggested_result);
> +	}
> +      else if (rhs_uhwi < (LONG_LONG_TYPE_SIZE - 1))
> +	{
> +	  loc.add_fixit_replace (lhs_loc, "1LL");
> +	  loc.add_fixit_replace (operator_loc, "<<");
> +	  warned = warning_at (&loc, OPT_Wxor_used_as_pow,
> +			       "result of %<%wu^%wu%> is %wu;"
> +			       " did you mean %<1LL << %wu%>?",
> +			       lhs_uhwi, rhs_uhwi, xor_result,
> +			       rhs_uhwi);
> +	}
> +      else if (rhs_uhwi <= LONG_LONG_TYPE_SIZE)
> +	warned = warning_at (&loc, OPT_Wxor_used_as_pow,
> +			     "result of %<%wu^%wu%> is %wu;"
> +			     " did you mean exponentiation?",
> +			     lhs_uhwi, rhs_uhwi, xor_result);
> +      /* Otherwise assume it's an xor.  */
> +    }
> +  else
> +    {
> +      gcc_assert (lhs_uhwi == 10);
> +      loc.add_fixit_replace (lhs_loc, "1");
> +      loc.add_fixit_replace (operator_loc, "e");
> +      warned = warning_at (&loc, OPT_Wxor_used_as_pow,
> +			   "result of %<%wu^%wu%> is %wu;"
> +			   " did you mean %<1e%wu%>?",
> +			   lhs_uhwi, rhs_uhwi, xor_result,
> +			   rhs_uhwi);
> +    }
> +  if (warned)
> +    {
> +      gcc_rich_location note_loc (lhs_loc);
> +      if (lhs_uhwi == 2)
> +	note_loc.add_fixit_replace (lhs_loc, "0x2");
> +      else
> +	{
> +	  gcc_assert (lhs_uhwi == 10);
> +	  note_loc.add_fixit_replace (lhs_loc, "0xa");
> +	}
> +      note_loc.fixits_cannot_be_auto_applied ();
> +      inform (&note_loc,
> +	      "you can silence this warning by using a hexadecimal constant"
> +	      " (%wx rather than %wd)",
> +	      lhs_uhwi, lhs_uhwi);
> +    }
> +}
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 44e1a60ce24..c11ea50ac42 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -1439,6 +1439,10 @@ Wwrite-strings
>   C ObjC C++ ObjC++ Var(warn_write_strings) Warning
>   In C++, nonzero means warn about deprecated conversion from string literals to 'char *'.  In C, similar warning, except that the conversion is of course not deprecated by the ISO C standard.
>   
> +Wxor-used-as-pow
> +C C++ Common Var(warn_xor_used_as_pow) Warning Init(1)
> +Warn about xor operators where it appears the user meant exponentiation.
> +
>   Wzero-as-null-pointer-constant
>   C++ ObjC++ Var(warn_zero_as_null_pointer_constant) Warning
>   Warn when a literal '0' is used as null pointer.
> diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
> index 92049d1a101..25064576829 100644
> --- a/gcc/c/c-parser.cc
> +++ b/gcc/c/c-parser.cc
> @@ -7496,6 +7496,7 @@ c_parser_string_literal (c_parser *parser, bool translate, bool wide_ok)
>     ret.original_code = STRING_CST;
>     ret.original_type = NULL_TREE;
>     set_c_expr_source_range (&ret, get_range_from_loc (line_table, loc));
> +  ret.m_decimal = 0;
>     parser->seen_string_literal = true;
>     return ret;
>   }
> @@ -7575,6 +7576,7 @@ c_parser_expr_no_commas (c_parser *parser, struct c_expr *after,
>     ret.value = build_modify_expr (op_location, lhs.value, lhs.original_type,
>   				 code, exp_location, rhs.value,
>   				 rhs.original_type);
> +  ret.m_decimal = 0;
>     set_c_expr_source_range (&ret, lhs.get_start (), rhs.get_finish ());
>     if (code == NOP_EXPR)
>       ret.original_code = MODIFY_EXPR;
> @@ -7712,6 +7714,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
>   			   : NULL);
>       }
>     set_c_expr_source_range (&ret, start, exp2.get_finish ());
> +  ret.m_decimal = 0;
>     return ret;
>   }
>   
> @@ -7901,6 +7904,7 @@ c_parser_binary_expression (c_parser *parser, struct c_expr *after,
>   	TREE_OPERAND (t, 0) = stack[0].expr.value;			      \
>   	TREE_OPERAND (t, 1) = stack[1].expr.value;			      \
>   	stack[0].expr.value = t;					      \
> +	stack[0].expr.m_decimal = 0;					      \
>         }									      \
>       else								      \
>         stack[sp - 1].expr = parser_build_binary_op (stack[sp].loc,	      \
> @@ -8187,6 +8191,7 @@ c_parser_unary_expression (c_parser *parser)
>   	ret.value = build_indirect_ref (combined_loc, op.value, RO_UNARY_STAR);
>   	ret.src_range.m_start = op_loc;
>   	ret.src_range.m_finish = finish;
> +	ret.m_decimal = 0;
>   	return ret;
>         }
>       case CPP_PLUS:
> @@ -8560,6 +8565,7 @@ c_parser_has_attribute_expression (c_parser *parser)
>       result.value =  boolean_false_node;
>   
>     set_c_expr_source_range (&result, start, finish);
> +  result.m_decimal = 0;
>     return result;
>   }
>   
> @@ -8925,6 +8931,7 @@ c_parser_predefined_identifier (c_parser *parser)
>     expr.value = fname_decl (loc, c_parser_peek_token (parser)->keyword,
>   			   c_parser_peek_token (parser)->value);
>     set_c_expr_source_range (&expr, loc, loc);
> +  expr.m_decimal = 0;
>     c_parser_consume_token (parser);
>     return expr;
>   }
> @@ -9003,12 +9010,14 @@ c_parser_postfix_expression (c_parser *parser)
>     source_range tok_range = c_parser_peek_token (parser)->get_range ();
>     expr.original_code = ERROR_MARK;
>     expr.original_type = NULL;
> +  expr.m_decimal = 0;
>     switch (c_parser_peek_token (parser)->type)
>       {
>       case CPP_NUMBER:
>         expr.value = c_parser_peek_token (parser)->value;
>         set_c_expr_source_range (&expr, tok_range);
>         loc = c_parser_peek_token (parser)->location;
> +      expr.m_decimal = c_parser_peek_token (parser)->flags & DECIMAL_INT;
>         c_parser_consume_token (parser);
>         if (TREE_CODE (expr.value) == FIXED_CST
>   	  && !targetm.fixed_point_supported_p ())
> diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
> index e655afd1cd4..b4231a17859 100644
> --- a/gcc/c/c-tree.h
> +++ b/gcc/c/c-tree.h
> @@ -147,6 +147,9 @@ struct c_expr
>        etc), so we stash a copy here.  */
>     source_range src_range;
>   
> +  /* True if this was directly from a decimal constant token.  */
> +  bool m_decimal : 1;
> +
>     /* Access to the first and last locations within the source spelling
>        of this expression.  */
>     location_t get_start () const { return src_range.m_start; }
> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> index 8514488b7a5..1f5fa07c925 100644
> --- a/gcc/c/c-typeck.cc
> +++ b/gcc/c/c-typeck.cc
> @@ -3821,6 +3821,7 @@ parser_build_binary_op (location_t location, enum tree_code code,
>   			struct c_expr arg1, struct c_expr arg2)
>   {
>     struct c_expr result;
> +  result.m_decimal = 0;
>   
>     enum tree_code code1 = arg1.original_code;
>     enum tree_code code2 = arg2.original_code;
> @@ -3978,6 +3979,14 @@ parser_build_binary_op (location_t location, enum tree_code code,
>   		"comparison between %qT and %qT",
>   		type1, type2);
>   
> +  if (warn_xor_used_as_pow
> +      && code == BIT_XOR_EXPR
> +      && arg1.m_decimal
> +      && arg2.m_decimal)
> +    check_for_xor_used_as_pow (arg1.get_location (), arg1.value,
> +			       location,
> +			       arg2.value);
> +
>     return result;
>   }
>   
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 3278b4114bd..cdff8282e64 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -54,13 +54,23 @@ class cp_expr
>   {
>   public:
>     cp_expr () :
> -    m_value (NULL), m_loc (UNKNOWN_LOCATION) {}
> +    m_value (NULL), m_loc (UNKNOWN_LOCATION),
> +    m_decimal (false)
> +  {}
>   
>     cp_expr (tree value) :
> -    m_value (value), m_loc (cp_expr_location (m_value)) {}
> +    m_value (value), m_loc (cp_expr_location (m_value)),
> +    m_decimal (false)
> +  {}
>   
>     cp_expr (tree value, location_t loc):
> -    m_value (value), m_loc (loc)
> +    m_value (value), m_loc (loc), m_decimal (false)
> +  {
> +    protected_set_expr_location (value, loc);
> +  }
> +
> +  cp_expr (tree value, location_t loc, bool decimal):
> +    m_value (value), m_loc (loc), m_decimal (decimal)
>     {
>       protected_set_expr_location (value, loc);
>     }
> @@ -102,9 +112,12 @@ public:
>       return *this;
>     }
>   
> +  bool decimal_p () const { return m_decimal; }
> +
>    private:
>     tree m_value;
>     location_t m_loc;
> +  bool m_decimal : 1;
>   };
>   
>   inline bool
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 33926d23179..73c46e79979 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -2066,6 +2066,8 @@ struct cp_parser_expression_stack_entry
>     enum cp_parser_prec prec;
>     /* Location of the binary operation we are parsing.  */
>     location_t loc;
> +  /* Flags from the operator token.  */
> +  unsigned char flags;
>   };
>   
>   /* The stack for storing partial expressions.  We only need NUM_PREC_VALUES
> @@ -5611,7 +5613,7 @@ cp_parser_primary_expression (cp_parser *parser,
>   	  if (!cast_p)
>   	    cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
>   	}
> -      return (cp_expr (token->u.value, token->location)
> +      return (cp_expr (token->u.value, token->location, token->flags & DECIMAL_INT)
>   	      .maybe_add_location_wrapper ());
>   
>       case CPP_CHAR_USERDEF:
> @@ -10157,6 +10159,7 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
>        get_rhs:
>         current.tree_type = binops_by_token[token->type].tree_type;
>         current.loc = token->location;
> +      current.flags = token->flags;
>   
>         /* We used the operator token.  */
>         cp_lexer_consume_token (parser->lexer);
> @@ -10241,6 +10244,18 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p,
>   	warn_logical_not_parentheses (current.loc, current.tree_type,
>   				      current.lhs, maybe_constant_value (rhs));
>   
> +      if (warn_xor_used_as_pow
> +	  && current.tree_type == BIT_XOR_EXPR
> +	  /* Don't warn for named "xor" (as opposed to '^').  */
> +	  && !(current.flags & NAMED_OP)
> +	  && current.lhs.decimal_p ()
> +	  && rhs.decimal_p ())
> +	check_for_xor_used_as_pow
> +	  (current.lhs.get_location (),
> +	   tree_strip_any_location_wrapper (current.lhs),
> +	   current.loc,
> +	   tree_strip_any_location_wrapper (rhs));
> +
>         overload = NULL;
>   
>         location_t combined_loc = make_location (current.loc,
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index a17c059d515..31516ded123 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -414,6 +414,7 @@ Objective-C and Objective-C++ Dialects}.
>   -Wvector-operation-performance @gol
>   -Wvla  -Wvla-larger-than=@var{byte-size}  -Wno-vla-larger-than @gol
>   -Wvolatile-register-var  -Wwrite-strings @gol
> +-Wxor-used-as-pow @gol
>   -Wzero-length-bounds}
>   
>   @item Static Analyzer Options
> @@ -9661,6 +9662,20 @@ modifier does not inhibit all optimizations that may eliminate reads
>   and/or writes to register variables.  This warning is enabled by
>   @option{-Wall}.
>   
> +@item -Wxor-used-as-pow @r{(C, C++, Objective-C and Objective-C++ only)}
> +@opindex Wxor-used-as-pow
> +@opindex Wno-xor-used-as-pow
> +Warn about uses of @code{^}, the exclusive or operator, where it appears
> +the user meant exponentiation.  Specifically, the warning occurs when the
> +left-hand side is the decimal constant 2 or 10 and the right-hand side
> +is also a decimal constant.
> +
> +In C and C++, @code{^} means exclusive or, whereas in some other languages
> +(e.g. TeX and some versions of BASIC) it means exponentiation.
> +
> +This warning is enabled by default.  It can be silenced by converting one
> +of the operands to hexadecimal.
> +
>   @item -Wdisabled-optimization
>   @opindex Wdisabled-optimization
>   @opindex Wno-disabled-optimization
> diff --git a/gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c
> new file mode 100644
> index 00000000000..962902c3a05
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c
> @@ -0,0 +1,57 @@
> +/* The precise output depends of sizeof(int) and sizeof(long long), so
> +   filter by target.  */
> +/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
> +
> +/* Apparent uses of ^ for powers of 2.  */
> +
> +short t2_16 = 2^16; /* { dg-warning "result of '2\\^16' is 18; did you mean '1 << 16' \\(65536\\)\\? \\\[-Wxor-used-as-pow\\\]" } */
> +int t2_17 = 2^17; /* { dg-warning "result of '2\\^17' is 19; did you mean '1 << 17' \\(131072\\)\\?" } */
> +int t2_30 = 2^30; /* { dg-warning "result of '2\\^30' is 28; did you mean '1 << 30' \\(1073741824\\)\\?" } */
> +
> +/* Should be 1LL at 31 and above, due to overflow.  */
> +int t2_31 = 2^31; /* { dg-warning "result of '2\\^31' is 29; did you mean '1LL << 31'\\?" } */
> +int t2_32 = 2^32; /* { dg-warning "result of '2\\^32' is 34; did you mean '1LL << 32'\\?" } */
> +
> +long t2_63 = 2^63; /* { dg-warning "result of '2\\^63' is 61; did you mean exponentiation\\?" } */
> +long t2_64 = 2^64; /* { dg-warning "result of '2\\^64' is 66; did you mean exponentiation\\?" } */
> +
> +/* ...but don't warn when RHS is large enough.  */
> +long t2_65 = 2^65;
> +long t2_127 = 2^127;
> +long t2_128 = 2^128;
> +long t2_129 = 2^129;
> +
> +/* Verify that -Wxor-used-as-pow happens before folding.  */
> +long t2_32_m1  = ((2^32)-1); /* { dg-warning "result of '2\\^32' is 34; did you mean '1LL << 32'\\?" } */
> +
> +
> +/* Apparent uses of ^ for powers of 10.  */
> +
> +long t10_2 = 10^2; /* { dg-warning "result of '10\\^2' is 8; did you mean '1e2'\\?" } */
> +long t10_9 = 10^9; /* { dg-warning "result of '10\\^9' is 3; did you mean '1e9'\\?" } */
> +long t10_10 = 10^10; /* { dg-warning "result of '10\\^10' is 0; did you mean '1e10'\\?" } */
> +long t10_100 = 10^100; /* { dg-warning "result of '10\\^100' is 110; did you mean '1e100'\\?" } */
> +
> +/* Don't warn on negatives.  */
> +long tm2_2 = -2^2;
> +long t2_m2 = 2^-2;
> +long tm10_10 = -10^10;
> +long t10_m10 = 10^-10;
> +
> +/* If LHS is not 2 or 10, we shouldn't complain.  */
> +int t0_0 = 0 ^ 0;
> +int t6_7 = 6 ^ 7;
> +
> +/* Floating point is already covered by type-checking.  */
> +float f10_10 = 10.f^10; /* { dg-error "invalid operands to binary \\^ \\(have 'float' and 'int'\\)" "" { target c } } */
> +/* { dg-error "invalid operands of types 'float' and 'int' to binary 'operator\\^'" "" { target c++ } .-1 } */
> +
> +/* Don't complain if the LHS isn't literal decimal 2 or 10.  */
> +int t1p1_16 = (1 + 1) ^ 16;
> +int t5p5_6 = (5 + 5) ^ 6;
> +int h2_8 = 0x2 ^ 8;
> +int h10_3 = 0xa ^ 3;
> +
> +/* Don't complain if the RHS isn't literal decimal.  */
> +int t2_x16 = 2^0x10;
> +int h10_x3 = 10 ^ 0x3;
> diff --git a/gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c
> new file mode 100644
> index 00000000000..f612b761c30
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c
> @@ -0,0 +1,34 @@
> +/* { dg-options "-fdiagnostics-show-caret" } */
> +
> +/* Test fixit hints for -Wxor-used-as-pow.  */
> +
> +int t2_8 = 2^8; /* { dg-line line_a } */
> +/* { dg-warning "result of '2\\^8' is 10; did you mean '1 << 8' \\(256\\)\\?" "warn" { target *-*-* } line_a } */
> +/* { dg-begin-multiline-output "" }
> + int t2_8 = 2^8;
> +             ^
> +            --
> +            1<<
> +   { dg-end-multiline-output "" } */
> +/* { dg-message "you can silence this warning by using a hexadecimal constant \\(0x2 rather than 2\\)" "note" { target *-*-* } line_a } */
> +/* { dg-begin-multiline-output "" }
> + int t2_8 = 2^8;
> +            ^
> +            0x2
> +   { dg-end-multiline-output "" } */
> +
> +
> +int t10_6 = 10^6; /* { dg-line line_b } */
> +/* { dg-warning "result of '10\\^6' is 12; did you mean '1e6'\\?"  "warn" { target *-*-* } line_b } */
> +/* { dg-begin-multiline-output "" }
> + int t10_6 = 10^6;
> +               ^
> +             ---
> +             1e
> +   { dg-end-multiline-output "" } */
> +/* { dg-message "you can silence this warning by using a hexadecimal constant \\(0xa rather than 10\\)" "note" { target *-*-* } line_b } */
> +/* { dg-begin-multiline-output "" }
> + int t10_6 = 10^6;
> +             ^~
> +             0xa
> +   { dg-end-multiline-output "" } */
> diff --git a/gcc/testsuite/g++.dg/parse/expr3.C b/gcc/testsuite/g++.dg/parse/expr3.C
> index 95d332f41b0..1e166733843 100644
> --- a/gcc/testsuite/g++.dg/parse/expr3.C
> +++ b/gcc/testsuite/g++.dg/parse/expr3.C
> @@ -23,7 +23,7 @@
>   
>   test (||, &&, 1, 1, 0, 0)
>   test (&&, |, 5, 1, 1, 19)
> -test (|, ^, 1, 2, 2, 1)
> +test (|, ^, 1, 2, 0x2, 1)
>   test (^, &, 1, 3, 2, 6)
>   test (&, ==, 1, 3, 2, 0)
>   test (==, <, 2, 0, 0, 0)
> diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-10.C b/gcc/testsuite/g++.dg/warn/Wparentheses-10.C
> index 557db091ad0..a5d28efcc2e 100644
> --- a/gcc/testsuite/g++.dg/warn/Wparentheses-10.C
> +++ b/gcc/testsuite/g++.dg/warn/Wparentheses-10.C
> @@ -16,7 +16,7 @@ bar (int a, int b, int c)
>     foo (1 & (2 ^ c));
>     foo (1 & 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 & 2) ^ 3);
> -  foo (1 & (2 ^ 3));
> +  foo (1 & (0x2 ^ 3));
>     foo (a ^ b & c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) & c);
>     foo (a ^ (b & c));
> @@ -34,7 +34,7 @@ bar (int a, int b, int c)
>     foo (1 + (2 ^ c));
>     foo (1 + 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 + 2) ^ 3);
> -  foo (1 + (2 ^ 3));
> +  foo (1 + (0x2 ^ 3));
>     foo (a ^ b + c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) + c);
>     foo (a ^ (b + c));
> @@ -52,7 +52,7 @@ bar (int a, int b, int c)
>     foo (1 - (2 ^ c));
>     foo (1 - 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 - 2) ^ 3);
> -  foo (1 - (2 ^ 3));
> +  foo (1 - (0x2 ^ 3));
>     foo (a ^ b - c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) - c);
>     foo (a ^ (b - c));
> @@ -70,7 +70,7 @@ bar (int a, int b, int c)
>     foo (1 >= (2 ^ c));
>     foo (1 >= 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 >= 2) ^ 3);
> -  foo (1 >= (2 ^ 3));
> +  foo (1 >= (0x2 ^ 3));
>     foo (a ^ b >= c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) >= c);
>     foo (a ^ (b >= c));
> @@ -88,7 +88,7 @@ bar (int a, int b, int c)
>     foo (1 == (2 ^ c));
>     foo (1 == 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 == 2) ^ 3);
> -  foo (1 == (2 ^ 3));
> +  foo (1 == (0x2 ^ 3));
>     foo (a ^ b == c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) == c);
>     foo (a ^ (b == c));
> @@ -104,9 +104,9 @@ bar (int a, int b, int c)
>     foo (1 < 2 ^ c); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 < 2) ^ c);
>     foo (1 < (2 ^ c));
> -  foo (1 < 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
> +  foo (1 < 0x2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 < 2) ^ 3);
> -  foo (1 < (2 ^ 3));
> +  foo (1 < (0x2 ^ 3));
>     foo (a ^ b < c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) < c);
>     foo (a ^ (b < c));
> diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-18.C b/gcc/testsuite/g++.dg/warn/Wparentheses-18.C
> index 83efaff4189..d50fad8ae80 100644
> --- a/gcc/testsuite/g++.dg/warn/Wparentheses-18.C
> +++ b/gcc/testsuite/g++.dg/warn/Wparentheses-18.C
> @@ -42,9 +42,9 @@ bar (T a, T b, T c)
>     foo (1 | 2 ^ c); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 | 2) ^ c);
>     foo (1 | (2 ^ c));
> -  foo (1 | 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
> +  foo (1 | 0x2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 | 2) ^ 3);
> -  foo (1 | (2 ^ 3));
> +  foo (1 | (0x2 ^ 3));
>     foo (a + b | c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a + b) | c);
>     foo (a + (b | c));
> diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-19.C b/gcc/testsuite/g++.dg/warn/Wparentheses-19.C
> index f0e2b805c9e..2ad8036ac84 100644
> --- a/gcc/testsuite/g++.dg/warn/Wparentheses-19.C
> +++ b/gcc/testsuite/g++.dg/warn/Wparentheses-19.C
> @@ -17,7 +17,7 @@ bar (T a, T b, T c)
>     foo (1 & (2 ^ c));
>     foo (1 & 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 & 2) ^ 3);
> -  foo (1 & (2 ^ 3));
> +  foo (1 & (0x2 ^ 3));
>     foo (a ^ b & c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) & c);
>     foo (a ^ (b & c));
> @@ -35,7 +35,7 @@ bar (T a, T b, T c)
>     foo (1 + (2 ^ c));
>     foo (1 + 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 + 2) ^ 3);
> -  foo (1 + (2 ^ 3));
> +  foo (1 + (0x2 ^ 3));
>     foo (a ^ b + c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) + c);
>     foo (a ^ (b + c));
> @@ -53,7 +53,7 @@ bar (T a, T b, T c)
>     foo (1 - (2 ^ c));
>     foo (1 - 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 - 2) ^ 3);
> -  foo (1 - (2 ^ 3));
> +  foo (1 - (0x2 ^ 3));
>     foo (a ^ b - c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) - c);
>     foo (a ^ (b - c));
> @@ -71,7 +71,7 @@ bar (T a, T b, T c)
>     foo (1 >= (2 ^ c));
>     foo (1 >= 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 >= 2) ^ 3);
> -  foo (1 >= (2 ^ 3));
> +  foo (1 >= (0x2 ^ 3));
>     foo (a ^ b >= c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) >= c);
>     foo (a ^ (b >= c));
> @@ -89,7 +89,7 @@ bar (T a, T b, T c)
>     foo (1 == (2 ^ c));
>     foo (1 == 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 == 2) ^ 3);
> -  foo (1 == (2 ^ 3));
> +  foo (1 == (0x2 ^ 3));
>     foo (a ^ b == c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) == c);
>     foo (a ^ (b == c));
> @@ -107,7 +107,7 @@ bar (T a, T b, T c)
>     foo (1 < (2 ^ c));
>     foo (1 < 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 < 2) ^ 3);
> -  foo (1 < (2 ^ 3));
> +  foo (1 < (0x2 ^ 3));
>     foo (a ^ b < c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a ^ b) < c);
>     foo (a ^ (b < c));
> diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-9.C b/gcc/testsuite/g++.dg/warn/Wparentheses-9.C
> index 7c8f01d327b..69976b3bacf 100644
> --- a/gcc/testsuite/g++.dg/warn/Wparentheses-9.C
> +++ b/gcc/testsuite/g++.dg/warn/Wparentheses-9.C
> @@ -41,9 +41,9 @@ bar (int a, int b, int c)
>     foo (1 | 2 ^ c); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 | 2) ^ c);
>     foo (1 | (2 ^ c));
> -  foo (1 | 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
> +  foo (1 | 0x2 ^ 3); // { dg-warning "parentheses" "correct warning" }
>     foo ((1 | 2) ^ 3);
> -  foo (1 | (2 ^ 3));
> +  foo (1 | (0x2 ^ 3));
>     foo (a + b | c); // { dg-warning "parentheses" "correct warning" }
>     foo ((a + b) | c);
>     foo (a + (b | c));
> diff --git a/gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C b/gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C
> new file mode 100644
> index 00000000000..4899d72b11b
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C
> @@ -0,0 +1,8 @@
> +/* Verify that -Wxor-used-as-pow only warns with ^ and not with
> +   named "xor".  */
> +
> +int t2_16 = 2^16; /* { dg-warning "result of '2\\^16' is 18; did you mean '1 << 16' \\(65536\\)\\?" } */
> +int t2x16 = 2 xor 16;
> +
> +int t10_6 = 10^6; /* { dg-warning "result of '10\\^6' is 12; did you mean '1e6'\\?" } */
> +int t10x6 = 10 xor 6;
> diff --git a/gcc/testsuite/gcc.dg/Wparentheses-6.c b/gcc/testsuite/gcc.dg/Wparentheses-6.c
> index 2d2cc161394..260b6adebea 100644
> --- a/gcc/testsuite/gcc.dg/Wparentheses-6.c
> +++ b/gcc/testsuite/gcc.dg/Wparentheses-6.c
> @@ -43,9 +43,9 @@ bar (int a, int b, int c)
>     foo (1 | 2 ^ c); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((1 | 2) ^ c);
>     foo (1 | (2 ^ c));
> -  foo (1 | 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
> +  foo (1 | 0x2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((1 | 2) ^ 3);
> -  foo (1 | (2 ^ 3));
> +  foo (1 | (0x2 ^ 3));
>     foo (a + b | c); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((a + b) | c);
>     foo (a + (b | c));
> diff --git a/gcc/testsuite/gcc.dg/Wparentheses-7.c b/gcc/testsuite/gcc.dg/Wparentheses-7.c
> index f3516969d23..6805094d296 100644
> --- a/gcc/testsuite/gcc.dg/Wparentheses-7.c
> +++ b/gcc/testsuite/gcc.dg/Wparentheses-7.c
> @@ -18,7 +18,7 @@ bar (int a, int b, int c)
>     foo (1 & (2 ^ c));
>     foo (1 & 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((1 & 2) ^ 3);
> -  foo (1 & (2 ^ 3));
> +  foo (1 & (0x2 ^ 3));
>     foo (a ^ b & c); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((a ^ b) & c);
>     foo (a ^ (b & c));
> @@ -36,7 +36,7 @@ bar (int a, int b, int c)
>     foo (1 + (2 ^ c));
>     foo (1 + 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((1 + 2) ^ 3);
> -  foo (1 + (2 ^ 3));
> +  foo (1 + (0x2 ^ 3));
>     foo (a ^ b + c); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((a ^ b) + c);
>     foo (a ^ (b + c));
> @@ -54,7 +54,7 @@ bar (int a, int b, int c)
>     foo (1 - (2 ^ c));
>     foo (1 - 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((1 - 2) ^ 3);
> -  foo (1 - (2 ^ 3));
> +  foo (1 - (0x2 ^ 3));
>     foo (a ^ b - c); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((a ^ b) - c);
>     foo (a ^ (b - c));
> @@ -72,7 +72,7 @@ bar (int a, int b, int c)
>     foo (1 >= (2 ^ c));
>     foo (1 >= 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((1 >= 2) ^ 3);
> -  foo (1 >= (2 ^ 3));
> +  foo (1 >= (0x2 ^ 3));
>     foo (a ^ b >= c); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((a ^ b) >= c);
>     foo (a ^ (b >= c));
> @@ -90,7 +90,7 @@ bar (int a, int b, int c)
>     foo (1 == (2 ^ c));
>     foo (1 == 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((1 == 2) ^ 3);
> -  foo (1 == (2 ^ 3));
> +  foo (1 == (0x2 ^ 3));
>     foo (a ^ b == c); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((a ^ b) == c);
>     foo (a ^ (b == c));
> @@ -108,7 +108,7 @@ bar (int a, int b, int c)
>     foo (1 < (2 ^ c));
>     foo (1 < 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((1 < 2) ^ 3);
> -  foo (1 < (2 ^ 3));
> +  foo (1 < (0x2 ^ 3));
>     foo (a ^ b < c); /* { dg-warning "parentheses" "correct warning" } */
>     foo ((a ^ b) < c);
>     foo (a ^ (b < c));
> diff --git a/gcc/testsuite/gcc.dg/precedence-1.c b/gcc/testsuite/gcc.dg/precedence-1.c
> index f3f1e352708..089cdfc1472 100644
> --- a/gcc/testsuite/gcc.dg/precedence-1.c
> +++ b/gcc/testsuite/gcc.dg/precedence-1.c
> @@ -135,10 +135,10 @@ f (void)
>     ASSERT_BIN (0, !=, 2, &, 1, 1, 1, 0);
>     ASSERT_BIN (1, &, 2, ==, 0, 0, 1, 0);
>     ASSERT_BIN (1, &, 2, !=, 0, 1, 0, 1);
> -  ASSERT_BIN (1, &, 2, ^, 3, 3, 3, 1);
> +  ASSERT_BIN (1, &, 0x2, ^, 3, 3, 3, 1);
>     ASSERT_BIN (3, ^, 2, &, 1, 3, 1, 3);
>     ASSERT_BIN (3, ^, 2, |, 1, 1, 1, 0);
> -  ASSERT_BIN (3, |, 2, ^, 1, 3, 2, 3);
> +  ASSERT_BIN (3, |, 0x2, ^, 1, 3, 2, 3);
>     ASSERT_BIN (2, |, 0, &&, 2, 1, 1, 2);
>     ASSERT_BIN (2, &&, 0, |, 2, 1, 2, 1);
>     ASSERT_BIN (0, &&, 0, ||, 1, 1, 1, 0);
> diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
> index 3eba6f74b57..f1e7c2b3b2a 100644
> --- a/libcpp/include/cpplib.h
> +++ b/libcpp/include/cpplib.h
> @@ -190,7 +190,7 @@ struct GTY(()) cpp_string {
>   #define NAMED_OP	(1 << 4) /* C++ named operators.  */
>   #define PREV_FALLTHROUGH (1 << 5) /* On a token preceeded by FALLTHROUGH
>   				     comment.  */
> -#define BOL		(1 << 6) /* Token at beginning of line.  */
> +#define DECIMAL_INT     (1 << 6) /* Decimal integer, set in c-lex.cc.  */
>   #define PURE_ZERO	(1 << 7) /* Single 0 digit, used by the C++ frontend,
>   				    set in c-lex.cc.  */
>   #define SP_DIGRAPH	(1 << 8) /* # or ## token was a digraph.  */
> @@ -199,6 +199,7 @@ struct GTY(()) cpp_string {
>   				    after a # operator.  */
>   #define NO_EXPAND	(1 << 10) /* Do not macro-expand this token.  */
>   #define PRAGMA_OP	(1 << 11) /* _Pragma token.  */
> +#define BOL		(1 << 12) /* Token at beginning of line.  */
>   
>   /* Specify which field, if any, of the cpp_token union is used.  */
>
  
Marek Polacek Aug. 30, 2022, 8:40 p.m. UTC | #2
This looks good to me, one thing though:

On Thu, Aug 11, 2022 at 09:38:12PM -0400, David Malcolm via Gcc-patches wrote:
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -1439,6 +1439,10 @@ Wwrite-strings
>  C ObjC C++ ObjC++ Var(warn_write_strings) Warning
>  In C++, nonzero means warn about deprecated conversion from string literals to 'char *'.  In C, similar warning, except that the conversion is of course not deprecated by the ISO C standard.
>  
> +Wxor-used-as-pow
> +C C++ Common Var(warn_xor_used_as_pow) Warning Init(1)

This doesn't include ObjC/ObjC++, but...

> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -414,6 +414,7 @@ Objective-C and Objective-C++ Dialects}.
>  -Wvector-operation-performance @gol
>  -Wvla  -Wvla-larger-than=@var{byte-size}  -Wno-vla-larger-than @gol
>  -Wvolatile-register-var  -Wwrite-strings @gol
> +-Wxor-used-as-pow @gol
>  -Wzero-length-bounds}
>  
>  @item Static Analyzer Options
> @@ -9661,6 +9662,20 @@ modifier does not inhibit all optimizations that may eliminate reads
>  and/or writes to register variables.  This warning is enabled by
>  @option{-Wall}.
>  
> +@item -Wxor-used-as-pow @r{(C, C++, Objective-C and Objective-C++ only)}

...here it includes Objective-C and Objective-C++.

Marek
  
David Malcolm Sept. 2, 2022, 10:39 p.m. UTC | #3
On Tue, 2022-08-30 at 16:40 -0400, Marek Polacek wrote:
> This looks good to me, one thing though:
> 
> On Thu, Aug 11, 2022 at 09:38:12PM -0400, David Malcolm via Gcc-
> patches wrote:
> > --- a/gcc/c-family/c.opt
> > +++ b/gcc/c-family/c.opt
> > @@ -1439,6 +1439,10 @@ Wwrite-strings
> >  C ObjC C++ ObjC++ Var(warn_write_strings) Warning
> >  In C++, nonzero means warn about deprecated conversion from string
> > literals to 'char *'.  In C, similar warning, except that the
> > conversion is of course not deprecated by the ISO C standard.
> >  
> > +Wxor-used-as-pow
> > +C C++ Common Var(warn_xor_used_as_pow) Warning Init(1)
> 
> This doesn't include ObjC/ObjC++, but...

I added ObjC/ObjC++ (and dropped Common), and retested it (and tested
it with -xobjective-c and -xobjective-c++);  I've pushed it to trunk as
r13-2386-gbedfca647a9e9c.

Thanks
Dave
  

Patch

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index c06769b6f0b..78ce94c6591 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1475,6 +1475,10 @@  extern tree do_warn_duplicated_branches_r (tree *, int *, void *);
 extern void warn_for_multistatement_macros (location_t, location_t,
 					    location_t, enum rid);
 
+extern void check_for_xor_used_as_pow (location_t lhs_loc, tree lhs_val,
+				       location_t operator_loc,
+				       tree rhs_val);
+
 /* In c-attribs.cc.  */
 extern bool attribute_takes_identifier_p (const_tree);
 extern tree handle_deprecated_attribute (tree *, tree, tree, int, bool *);
diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
index 8bfa4f4024f..1eda6f1fccc 100644
--- a/gcc/c-family/c-lex.cc
+++ b/gcc/c-family/c-lex.cc
@@ -509,7 +509,11 @@  c_lex_with_flags (tree *value, location_t *loc, unsigned char *cpp_flags,
 	    /* C++ uses '0' to mark virtual functions as pure.
 	       Set PURE_ZERO to pass this information to the C++ parser.  */
 	    if (tok->val.str.len == 1 && *tok->val.str.text == '0')
-	      add_flags = PURE_ZERO;
+	      add_flags = PURE_ZERO | DECIMAL_INT;
+	    else if ((flags & CPP_N_INTEGER) && (flags & CPP_N_DECIMAL))
+	      /* -Wxor-used-as-pow is only active for LHS of ^ expressed
+		 as a decimal integer. */
+	      add_flags = DECIMAL_INT;
 	    *value = interpret_integer (tok, flags, &overflow);
 	    break;
 
diff --git a/gcc/c-family/c-warn.cc b/gcc/c-family/c-warn.cc
index ea7335f3edf..ed79cc3ca40 100644
--- a/gcc/c-family/c-warn.cc
+++ b/gcc/c-family/c-warn.cc
@@ -3799,3 +3799,97 @@  do_warn_array_compare (location_t location, tree_code code, tree op0, tree op1)
 		op0, op_symbol_code (code), op1);
     }
 }
+
+/* Given LHS_VAL ^ RHS_VAL, where LHS_LOC is the location of the LHS and
+   OPERATOR_LOC is the location of the ^, complain with -Wxor-used-as-pow
+   if it looks like the user meant exponentiation rather than xor.  */
+
+void
+check_for_xor_used_as_pow (location_t lhs_loc, tree lhs_val,
+			   location_t operator_loc,
+			   tree rhs_val)
+{
+  /* Only complain if both args are non-negative integer constants.  */
+  if (!(TREE_CODE (lhs_val) == INTEGER_CST
+	&& tree_int_cst_sgn (lhs_val) >= 0))
+    return;
+  if (!(TREE_CODE (rhs_val) == INTEGER_CST
+	&& tree_int_cst_sgn (rhs_val) >= 0))
+    return;
+
+  /* Only complain if the LHS is 2 or 10.  */
+  unsigned HOST_WIDE_INT lhs_uhwi = tree_to_uhwi (lhs_val);
+  if (lhs_uhwi != 2 && lhs_uhwi != 10)
+    return;
+
+  unsigned HOST_WIDE_INT rhs_uhwi = tree_to_uhwi (rhs_val);
+  unsigned HOST_WIDE_INT xor_result = lhs_uhwi ^ rhs_uhwi;
+  binary_op_rich_location loc (operator_loc,
+			       lhs_val, rhs_val, false);
+
+  /* If we issue fix-it hints with the warning then we will also issue a
+     note suggesting how to suppress the warning with a different change.
+     These proposed changes are incompatible.  */
+  loc.fixits_cannot_be_auto_applied ();
+
+  auto_diagnostic_group d;
+  bool warned = false;
+  if (lhs_uhwi == 2)
+    {
+      /* Would exponentiation fit in int, in long long, or not at all?  */
+      if (rhs_uhwi < (INT_TYPE_SIZE - 1))
+	{
+	  unsigned HOST_WIDE_INT suggested_result = 1 << rhs_uhwi;
+	  loc.add_fixit_replace (lhs_loc, "1");
+	  loc.add_fixit_replace (operator_loc, "<<");
+	  warned = warning_at (&loc, OPT_Wxor_used_as_pow,
+			       "result of %<%wu^%wu%> is %wu;"
+			       " did you mean %<1 << %wu%> (%wu)?",
+			       lhs_uhwi, rhs_uhwi, xor_result,
+			       rhs_uhwi, suggested_result);
+	}
+      else if (rhs_uhwi < (LONG_LONG_TYPE_SIZE - 1))
+	{
+	  loc.add_fixit_replace (lhs_loc, "1LL");
+	  loc.add_fixit_replace (operator_loc, "<<");
+	  warned = warning_at (&loc, OPT_Wxor_used_as_pow,
+			       "result of %<%wu^%wu%> is %wu;"
+			       " did you mean %<1LL << %wu%>?",
+			       lhs_uhwi, rhs_uhwi, xor_result,
+			       rhs_uhwi);
+	}
+      else if (rhs_uhwi <= LONG_LONG_TYPE_SIZE)
+	warned = warning_at (&loc, OPT_Wxor_used_as_pow,
+			     "result of %<%wu^%wu%> is %wu;"
+			     " did you mean exponentiation?",
+			     lhs_uhwi, rhs_uhwi, xor_result);
+      /* Otherwise assume it's an xor.  */
+    }
+  else
+    {
+      gcc_assert (lhs_uhwi == 10);
+      loc.add_fixit_replace (lhs_loc, "1");
+      loc.add_fixit_replace (operator_loc, "e");
+      warned = warning_at (&loc, OPT_Wxor_used_as_pow,
+			   "result of %<%wu^%wu%> is %wu;"
+			   " did you mean %<1e%wu%>?",
+			   lhs_uhwi, rhs_uhwi, xor_result,
+			   rhs_uhwi);
+    }
+  if (warned)
+    {
+      gcc_rich_location note_loc (lhs_loc);
+      if (lhs_uhwi == 2)
+	note_loc.add_fixit_replace (lhs_loc, "0x2");
+      else
+	{
+	  gcc_assert (lhs_uhwi == 10);
+	  note_loc.add_fixit_replace (lhs_loc, "0xa");
+	}
+      note_loc.fixits_cannot_be_auto_applied ();
+      inform (&note_loc,
+	      "you can silence this warning by using a hexadecimal constant"
+	      " (%wx rather than %wd)",
+	      lhs_uhwi, lhs_uhwi);
+    }
+}
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 44e1a60ce24..c11ea50ac42 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1439,6 +1439,10 @@  Wwrite-strings
 C ObjC C++ ObjC++ Var(warn_write_strings) Warning
 In C++, nonzero means warn about deprecated conversion from string literals to 'char *'.  In C, similar warning, except that the conversion is of course not deprecated by the ISO C standard.
 
+Wxor-used-as-pow
+C C++ Common Var(warn_xor_used_as_pow) Warning Init(1)
+Warn about xor operators where it appears the user meant exponentiation.
+
 Wzero-as-null-pointer-constant
 C++ ObjC++ Var(warn_zero_as_null_pointer_constant) Warning
 Warn when a literal '0' is used as null pointer.
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 92049d1a101..25064576829 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -7496,6 +7496,7 @@  c_parser_string_literal (c_parser *parser, bool translate, bool wide_ok)
   ret.original_code = STRING_CST;
   ret.original_type = NULL_TREE;
   set_c_expr_source_range (&ret, get_range_from_loc (line_table, loc));
+  ret.m_decimal = 0;
   parser->seen_string_literal = true;
   return ret;
 }
@@ -7575,6 +7576,7 @@  c_parser_expr_no_commas (c_parser *parser, struct c_expr *after,
   ret.value = build_modify_expr (op_location, lhs.value, lhs.original_type,
 				 code, exp_location, rhs.value,
 				 rhs.original_type);
+  ret.m_decimal = 0;
   set_c_expr_source_range (&ret, lhs.get_start (), rhs.get_finish ());
   if (code == NOP_EXPR)
     ret.original_code = MODIFY_EXPR;
@@ -7712,6 +7714,7 @@  c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
 			   : NULL);
     }
   set_c_expr_source_range (&ret, start, exp2.get_finish ());
+  ret.m_decimal = 0;
   return ret;
 }
 
@@ -7901,6 +7904,7 @@  c_parser_binary_expression (c_parser *parser, struct c_expr *after,
 	TREE_OPERAND (t, 0) = stack[0].expr.value;			      \
 	TREE_OPERAND (t, 1) = stack[1].expr.value;			      \
 	stack[0].expr.value = t;					      \
+	stack[0].expr.m_decimal = 0;					      \
       }									      \
     else								      \
       stack[sp - 1].expr = parser_build_binary_op (stack[sp].loc,	      \
@@ -8187,6 +8191,7 @@  c_parser_unary_expression (c_parser *parser)
 	ret.value = build_indirect_ref (combined_loc, op.value, RO_UNARY_STAR);
 	ret.src_range.m_start = op_loc;
 	ret.src_range.m_finish = finish;
+	ret.m_decimal = 0;
 	return ret;
       }
     case CPP_PLUS:
@@ -8560,6 +8565,7 @@  c_parser_has_attribute_expression (c_parser *parser)
     result.value =  boolean_false_node;
 
   set_c_expr_source_range (&result, start, finish);
+  result.m_decimal = 0;
   return result;
 }
 
@@ -8925,6 +8931,7 @@  c_parser_predefined_identifier (c_parser *parser)
   expr.value = fname_decl (loc, c_parser_peek_token (parser)->keyword,
 			   c_parser_peek_token (parser)->value);
   set_c_expr_source_range (&expr, loc, loc);
+  expr.m_decimal = 0;
   c_parser_consume_token (parser);
   return expr;
 }
@@ -9003,12 +9010,14 @@  c_parser_postfix_expression (c_parser *parser)
   source_range tok_range = c_parser_peek_token (parser)->get_range ();
   expr.original_code = ERROR_MARK;
   expr.original_type = NULL;
+  expr.m_decimal = 0;
   switch (c_parser_peek_token (parser)->type)
     {
     case CPP_NUMBER:
       expr.value = c_parser_peek_token (parser)->value;
       set_c_expr_source_range (&expr, tok_range);
       loc = c_parser_peek_token (parser)->location;
+      expr.m_decimal = c_parser_peek_token (parser)->flags & DECIMAL_INT;
       c_parser_consume_token (parser);
       if (TREE_CODE (expr.value) == FIXED_CST
 	  && !targetm.fixed_point_supported_p ())
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index e655afd1cd4..b4231a17859 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -147,6 +147,9 @@  struct c_expr
      etc), so we stash a copy here.  */
   source_range src_range;
 
+  /* True if this was directly from a decimal constant token.  */
+  bool m_decimal : 1;
+
   /* Access to the first and last locations within the source spelling
      of this expression.  */
   location_t get_start () const { return src_range.m_start; }
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 8514488b7a5..1f5fa07c925 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -3821,6 +3821,7 @@  parser_build_binary_op (location_t location, enum tree_code code,
 			struct c_expr arg1, struct c_expr arg2)
 {
   struct c_expr result;
+  result.m_decimal = 0;
 
   enum tree_code code1 = arg1.original_code;
   enum tree_code code2 = arg2.original_code;
@@ -3978,6 +3979,14 @@  parser_build_binary_op (location_t location, enum tree_code code,
 		"comparison between %qT and %qT",
 		type1, type2);
 
+  if (warn_xor_used_as_pow
+      && code == BIT_XOR_EXPR
+      && arg1.m_decimal
+      && arg2.m_decimal)
+    check_for_xor_used_as_pow (arg1.get_location (), arg1.value,
+			       location,
+			       arg2.value);
+
   return result;
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3278b4114bd..cdff8282e64 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -54,13 +54,23 @@  class cp_expr
 {
 public:
   cp_expr () :
-    m_value (NULL), m_loc (UNKNOWN_LOCATION) {}
+    m_value (NULL), m_loc (UNKNOWN_LOCATION),
+    m_decimal (false)
+  {}
 
   cp_expr (tree value) :
-    m_value (value), m_loc (cp_expr_location (m_value)) {}
+    m_value (value), m_loc (cp_expr_location (m_value)),
+    m_decimal (false)
+  {}
 
   cp_expr (tree value, location_t loc):
-    m_value (value), m_loc (loc)
+    m_value (value), m_loc (loc), m_decimal (false)
+  {
+    protected_set_expr_location (value, loc);
+  }
+
+  cp_expr (tree value, location_t loc, bool decimal):
+    m_value (value), m_loc (loc), m_decimal (decimal)
   {
     protected_set_expr_location (value, loc);
   }
@@ -102,9 +112,12 @@  public:
     return *this;
   }
 
+  bool decimal_p () const { return m_decimal; }
+
  private:
   tree m_value;
   location_t m_loc;
+  bool m_decimal : 1;
 };
 
 inline bool
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 33926d23179..73c46e79979 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -2066,6 +2066,8 @@  struct cp_parser_expression_stack_entry
   enum cp_parser_prec prec;
   /* Location of the binary operation we are parsing.  */
   location_t loc;
+  /* Flags from the operator token.  */
+  unsigned char flags;
 };
 
 /* The stack for storing partial expressions.  We only need NUM_PREC_VALUES
@@ -5611,7 +5613,7 @@  cp_parser_primary_expression (cp_parser *parser,
 	  if (!cast_p)
 	    cp_parser_non_integral_constant_expression (parser, NIC_FLOAT);
 	}
-      return (cp_expr (token->u.value, token->location)
+      return (cp_expr (token->u.value, token->location, token->flags & DECIMAL_INT)
 	      .maybe_add_location_wrapper ());
 
     case CPP_CHAR_USERDEF:
@@ -10157,6 +10159,7 @@  cp_parser_binary_expression (cp_parser* parser, bool cast_p,
      get_rhs:
       current.tree_type = binops_by_token[token->type].tree_type;
       current.loc = token->location;
+      current.flags = token->flags;
 
       /* We used the operator token.  */
       cp_lexer_consume_token (parser->lexer);
@@ -10241,6 +10244,18 @@  cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 	warn_logical_not_parentheses (current.loc, current.tree_type,
 				      current.lhs, maybe_constant_value (rhs));
 
+      if (warn_xor_used_as_pow
+	  && current.tree_type == BIT_XOR_EXPR
+	  /* Don't warn for named "xor" (as opposed to '^').  */
+	  && !(current.flags & NAMED_OP)
+	  && current.lhs.decimal_p ()
+	  && rhs.decimal_p ())
+	check_for_xor_used_as_pow
+	  (current.lhs.get_location (),
+	   tree_strip_any_location_wrapper (current.lhs),
+	   current.loc,
+	   tree_strip_any_location_wrapper (rhs));
+
       overload = NULL;
 
       location_t combined_loc = make_location (current.loc,
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a17c059d515..31516ded123 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -414,6 +414,7 @@  Objective-C and Objective-C++ Dialects}.
 -Wvector-operation-performance @gol
 -Wvla  -Wvla-larger-than=@var{byte-size}  -Wno-vla-larger-than @gol
 -Wvolatile-register-var  -Wwrite-strings @gol
+-Wxor-used-as-pow @gol
 -Wzero-length-bounds}
 
 @item Static Analyzer Options
@@ -9661,6 +9662,20 @@  modifier does not inhibit all optimizations that may eliminate reads
 and/or writes to register variables.  This warning is enabled by
 @option{-Wall}.
 
+@item -Wxor-used-as-pow @r{(C, C++, Objective-C and Objective-C++ only)}
+@opindex Wxor-used-as-pow
+@opindex Wno-xor-used-as-pow
+Warn about uses of @code{^}, the exclusive or operator, where it appears
+the user meant exponentiation.  Specifically, the warning occurs when the
+left-hand side is the decimal constant 2 or 10 and the right-hand side
+is also a decimal constant.
+
+In C and C++, @code{^} means exclusive or, whereas in some other languages
+(e.g. TeX and some versions of BASIC) it means exponentiation.
+
+This warning is enabled by default.  It can be silenced by converting one
+of the operands to hexadecimal.
+
 @item -Wdisabled-optimization
 @opindex Wdisabled-optimization
 @opindex Wno-disabled-optimization
diff --git a/gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c
new file mode 100644
index 00000000000..962902c3a05
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-1.c
@@ -0,0 +1,57 @@ 
+/* The precise output depends of sizeof(int) and sizeof(long long), so
+   filter by target.  */
+/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
+
+/* Apparent uses of ^ for powers of 2.  */
+
+short t2_16 = 2^16; /* { dg-warning "result of '2\\^16' is 18; did you mean '1 << 16' \\(65536\\)\\? \\\[-Wxor-used-as-pow\\\]" } */
+int t2_17 = 2^17; /* { dg-warning "result of '2\\^17' is 19; did you mean '1 << 17' \\(131072\\)\\?" } */
+int t2_30 = 2^30; /* { dg-warning "result of '2\\^30' is 28; did you mean '1 << 30' \\(1073741824\\)\\?" } */
+
+/* Should be 1LL at 31 and above, due to overflow.  */
+int t2_31 = 2^31; /* { dg-warning "result of '2\\^31' is 29; did you mean '1LL << 31'\\?" } */
+int t2_32 = 2^32; /* { dg-warning "result of '2\\^32' is 34; did you mean '1LL << 32'\\?" } */
+
+long t2_63 = 2^63; /* { dg-warning "result of '2\\^63' is 61; did you mean exponentiation\\?" } */
+long t2_64 = 2^64; /* { dg-warning "result of '2\\^64' is 66; did you mean exponentiation\\?" } */
+
+/* ...but don't warn when RHS is large enough.  */
+long t2_65 = 2^65;
+long t2_127 = 2^127;
+long t2_128 = 2^128;
+long t2_129 = 2^129;
+
+/* Verify that -Wxor-used-as-pow happens before folding.  */
+long t2_32_m1  = ((2^32)-1); /* { dg-warning "result of '2\\^32' is 34; did you mean '1LL << 32'\\?" } */
+
+
+/* Apparent uses of ^ for powers of 10.  */
+
+long t10_2 = 10^2; /* { dg-warning "result of '10\\^2' is 8; did you mean '1e2'\\?" } */
+long t10_9 = 10^9; /* { dg-warning "result of '10\\^9' is 3; did you mean '1e9'\\?" } */
+long t10_10 = 10^10; /* { dg-warning "result of '10\\^10' is 0; did you mean '1e10'\\?" } */
+long t10_100 = 10^100; /* { dg-warning "result of '10\\^100' is 110; did you mean '1e100'\\?" } */
+
+/* Don't warn on negatives.  */
+long tm2_2 = -2^2;
+long t2_m2 = 2^-2;
+long tm10_10 = -10^10;
+long t10_m10 = 10^-10;
+
+/* If LHS is not 2 or 10, we shouldn't complain.  */
+int t0_0 = 0 ^ 0;
+int t6_7 = 6 ^ 7;
+
+/* Floating point is already covered by type-checking.  */
+float f10_10 = 10.f^10; /* { dg-error "invalid operands to binary \\^ \\(have 'float' and 'int'\\)" "" { target c } } */
+/* { dg-error "invalid operands of types 'float' and 'int' to binary 'operator\\^'" "" { target c++ } .-1 } */
+
+/* Don't complain if the LHS isn't literal decimal 2 or 10.  */
+int t1p1_16 = (1 + 1) ^ 16;
+int t5p5_6 = (5 + 5) ^ 6;
+int h2_8 = 0x2 ^ 8;
+int h10_3 = 0xa ^ 3;
+
+/* Don't complain if the RHS isn't literal decimal.  */
+int t2_x16 = 2^0x10;
+int h10_x3 = 10 ^ 0x3;
diff --git a/gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c
new file mode 100644
index 00000000000..f612b761c30
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wxor-used-as-pow-fixits.c
@@ -0,0 +1,34 @@ 
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+/* Test fixit hints for -Wxor-used-as-pow.  */
+
+int t2_8 = 2^8; /* { dg-line line_a } */
+/* { dg-warning "result of '2\\^8' is 10; did you mean '1 << 8' \\(256\\)\\?" "warn" { target *-*-* } line_a } */
+/* { dg-begin-multiline-output "" }
+ int t2_8 = 2^8;
+             ^
+            --
+            1<<
+   { dg-end-multiline-output "" } */
+/* { dg-message "you can silence this warning by using a hexadecimal constant \\(0x2 rather than 2\\)" "note" { target *-*-* } line_a } */
+/* { dg-begin-multiline-output "" }
+ int t2_8 = 2^8;
+            ^
+            0x2
+   { dg-end-multiline-output "" } */
+
+
+int t10_6 = 10^6; /* { dg-line line_b } */
+/* { dg-warning "result of '10\\^6' is 12; did you mean '1e6'\\?"  "warn" { target *-*-* } line_b } */
+/* { dg-begin-multiline-output "" }
+ int t10_6 = 10^6;
+               ^
+             ---
+             1e
+   { dg-end-multiline-output "" } */
+/* { dg-message "you can silence this warning by using a hexadecimal constant \\(0xa rather than 10\\)" "note" { target *-*-* } line_b } */
+/* { dg-begin-multiline-output "" }
+ int t10_6 = 10^6;
+             ^~
+             0xa
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/g++.dg/parse/expr3.C b/gcc/testsuite/g++.dg/parse/expr3.C
index 95d332f41b0..1e166733843 100644
--- a/gcc/testsuite/g++.dg/parse/expr3.C
+++ b/gcc/testsuite/g++.dg/parse/expr3.C
@@ -23,7 +23,7 @@ 
 
 test (||, &&, 1, 1, 0, 0)
 test (&&, |, 5, 1, 1, 19)
-test (|, ^, 1, 2, 2, 1)
+test (|, ^, 1, 2, 0x2, 1)
 test (^, &, 1, 3, 2, 6)
 test (&, ==, 1, 3, 2, 0)
 test (==, <, 2, 0, 0, 0)
diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-10.C b/gcc/testsuite/g++.dg/warn/Wparentheses-10.C
index 557db091ad0..a5d28efcc2e 100644
--- a/gcc/testsuite/g++.dg/warn/Wparentheses-10.C
+++ b/gcc/testsuite/g++.dg/warn/Wparentheses-10.C
@@ -16,7 +16,7 @@  bar (int a, int b, int c)
   foo (1 & (2 ^ c));
   foo (1 & 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 & 2) ^ 3);
-  foo (1 & (2 ^ 3));
+  foo (1 & (0x2 ^ 3));
   foo (a ^ b & c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) & c);
   foo (a ^ (b & c));
@@ -34,7 +34,7 @@  bar (int a, int b, int c)
   foo (1 + (2 ^ c));
   foo (1 + 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 + 2) ^ 3);
-  foo (1 + (2 ^ 3));
+  foo (1 + (0x2 ^ 3));
   foo (a ^ b + c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) + c);
   foo (a ^ (b + c));
@@ -52,7 +52,7 @@  bar (int a, int b, int c)
   foo (1 - (2 ^ c));
   foo (1 - 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 - 2) ^ 3);
-  foo (1 - (2 ^ 3));
+  foo (1 - (0x2 ^ 3));
   foo (a ^ b - c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) - c);
   foo (a ^ (b - c));
@@ -70,7 +70,7 @@  bar (int a, int b, int c)
   foo (1 >= (2 ^ c));
   foo (1 >= 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 >= 2) ^ 3);
-  foo (1 >= (2 ^ 3));
+  foo (1 >= (0x2 ^ 3));
   foo (a ^ b >= c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) >= c);
   foo (a ^ (b >= c));
@@ -88,7 +88,7 @@  bar (int a, int b, int c)
   foo (1 == (2 ^ c));
   foo (1 == 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 == 2) ^ 3);
-  foo (1 == (2 ^ 3));
+  foo (1 == (0x2 ^ 3));
   foo (a ^ b == c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) == c);
   foo (a ^ (b == c));
@@ -104,9 +104,9 @@  bar (int a, int b, int c)
   foo (1 < 2 ^ c); // { dg-warning "parentheses" "correct warning" }
   foo ((1 < 2) ^ c);
   foo (1 < (2 ^ c));
-  foo (1 < 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
+  foo (1 < 0x2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 < 2) ^ 3);
-  foo (1 < (2 ^ 3));
+  foo (1 < (0x2 ^ 3));
   foo (a ^ b < c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) < c);
   foo (a ^ (b < c));
diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-18.C b/gcc/testsuite/g++.dg/warn/Wparentheses-18.C
index 83efaff4189..d50fad8ae80 100644
--- a/gcc/testsuite/g++.dg/warn/Wparentheses-18.C
+++ b/gcc/testsuite/g++.dg/warn/Wparentheses-18.C
@@ -42,9 +42,9 @@  bar (T a, T b, T c)
   foo (1 | 2 ^ c); // { dg-warning "parentheses" "correct warning" }
   foo ((1 | 2) ^ c);
   foo (1 | (2 ^ c));
-  foo (1 | 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
+  foo (1 | 0x2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 | 2) ^ 3);
-  foo (1 | (2 ^ 3));
+  foo (1 | (0x2 ^ 3));
   foo (a + b | c); // { dg-warning "parentheses" "correct warning" }
   foo ((a + b) | c);
   foo (a + (b | c));
diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-19.C b/gcc/testsuite/g++.dg/warn/Wparentheses-19.C
index f0e2b805c9e..2ad8036ac84 100644
--- a/gcc/testsuite/g++.dg/warn/Wparentheses-19.C
+++ b/gcc/testsuite/g++.dg/warn/Wparentheses-19.C
@@ -17,7 +17,7 @@  bar (T a, T b, T c)
   foo (1 & (2 ^ c));
   foo (1 & 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 & 2) ^ 3);
-  foo (1 & (2 ^ 3));
+  foo (1 & (0x2 ^ 3));
   foo (a ^ b & c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) & c);
   foo (a ^ (b & c));
@@ -35,7 +35,7 @@  bar (T a, T b, T c)
   foo (1 + (2 ^ c));
   foo (1 + 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 + 2) ^ 3);
-  foo (1 + (2 ^ 3));
+  foo (1 + (0x2 ^ 3));
   foo (a ^ b + c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) + c);
   foo (a ^ (b + c));
@@ -53,7 +53,7 @@  bar (T a, T b, T c)
   foo (1 - (2 ^ c));
   foo (1 - 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 - 2) ^ 3);
-  foo (1 - (2 ^ 3));
+  foo (1 - (0x2 ^ 3));
   foo (a ^ b - c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) - c);
   foo (a ^ (b - c));
@@ -71,7 +71,7 @@  bar (T a, T b, T c)
   foo (1 >= (2 ^ c));
   foo (1 >= 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 >= 2) ^ 3);
-  foo (1 >= (2 ^ 3));
+  foo (1 >= (0x2 ^ 3));
   foo (a ^ b >= c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) >= c);
   foo (a ^ (b >= c));
@@ -89,7 +89,7 @@  bar (T a, T b, T c)
   foo (1 == (2 ^ c));
   foo (1 == 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 == 2) ^ 3);
-  foo (1 == (2 ^ 3));
+  foo (1 == (0x2 ^ 3));
   foo (a ^ b == c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) == c);
   foo (a ^ (b == c));
@@ -107,7 +107,7 @@  bar (T a, T b, T c)
   foo (1 < (2 ^ c));
   foo (1 < 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 < 2) ^ 3);
-  foo (1 < (2 ^ 3));
+  foo (1 < (0x2 ^ 3));
   foo (a ^ b < c); // { dg-warning "parentheses" "correct warning" }
   foo ((a ^ b) < c);
   foo (a ^ (b < c));
diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-9.C b/gcc/testsuite/g++.dg/warn/Wparentheses-9.C
index 7c8f01d327b..69976b3bacf 100644
--- a/gcc/testsuite/g++.dg/warn/Wparentheses-9.C
+++ b/gcc/testsuite/g++.dg/warn/Wparentheses-9.C
@@ -41,9 +41,9 @@  bar (int a, int b, int c)
   foo (1 | 2 ^ c); // { dg-warning "parentheses" "correct warning" }
   foo ((1 | 2) ^ c);
   foo (1 | (2 ^ c));
-  foo (1 | 2 ^ 3); // { dg-warning "parentheses" "correct warning" }
+  foo (1 | 0x2 ^ 3); // { dg-warning "parentheses" "correct warning" }
   foo ((1 | 2) ^ 3);
-  foo (1 | (2 ^ 3));
+  foo (1 | (0x2 ^ 3));
   foo (a + b | c); // { dg-warning "parentheses" "correct warning" }
   foo ((a + b) | c);
   foo (a + (b | c));
diff --git a/gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C b/gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C
new file mode 100644
index 00000000000..4899d72b11b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wxor-used-as-pow-named-op.C
@@ -0,0 +1,8 @@ 
+/* Verify that -Wxor-used-as-pow only warns with ^ and not with
+   named "xor".  */
+
+int t2_16 = 2^16; /* { dg-warning "result of '2\\^16' is 18; did you mean '1 << 16' \\(65536\\)\\?" } */
+int t2x16 = 2 xor 16;
+
+int t10_6 = 10^6; /* { dg-warning "result of '10\\^6' is 12; did you mean '1e6'\\?" } */
+int t10x6 = 10 xor 6;
diff --git a/gcc/testsuite/gcc.dg/Wparentheses-6.c b/gcc/testsuite/gcc.dg/Wparentheses-6.c
index 2d2cc161394..260b6adebea 100644
--- a/gcc/testsuite/gcc.dg/Wparentheses-6.c
+++ b/gcc/testsuite/gcc.dg/Wparentheses-6.c
@@ -43,9 +43,9 @@  bar (int a, int b, int c)
   foo (1 | 2 ^ c); /* { dg-warning "parentheses" "correct warning" } */
   foo ((1 | 2) ^ c);
   foo (1 | (2 ^ c));
-  foo (1 | 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
+  foo (1 | 0x2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
   foo ((1 | 2) ^ 3);
-  foo (1 | (2 ^ 3));
+  foo (1 | (0x2 ^ 3));
   foo (a + b | c); /* { dg-warning "parentheses" "correct warning" } */
   foo ((a + b) | c);
   foo (a + (b | c));
diff --git a/gcc/testsuite/gcc.dg/Wparentheses-7.c b/gcc/testsuite/gcc.dg/Wparentheses-7.c
index f3516969d23..6805094d296 100644
--- a/gcc/testsuite/gcc.dg/Wparentheses-7.c
+++ b/gcc/testsuite/gcc.dg/Wparentheses-7.c
@@ -18,7 +18,7 @@  bar (int a, int b, int c)
   foo (1 & (2 ^ c));
   foo (1 & 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
   foo ((1 & 2) ^ 3);
-  foo (1 & (2 ^ 3));
+  foo (1 & (0x2 ^ 3));
   foo (a ^ b & c); /* { dg-warning "parentheses" "correct warning" } */
   foo ((a ^ b) & c);
   foo (a ^ (b & c));
@@ -36,7 +36,7 @@  bar (int a, int b, int c)
   foo (1 + (2 ^ c));
   foo (1 + 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
   foo ((1 + 2) ^ 3);
-  foo (1 + (2 ^ 3));
+  foo (1 + (0x2 ^ 3));
   foo (a ^ b + c); /* { dg-warning "parentheses" "correct warning" } */
   foo ((a ^ b) + c);
   foo (a ^ (b + c));
@@ -54,7 +54,7 @@  bar (int a, int b, int c)
   foo (1 - (2 ^ c));
   foo (1 - 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
   foo ((1 - 2) ^ 3);
-  foo (1 - (2 ^ 3));
+  foo (1 - (0x2 ^ 3));
   foo (a ^ b - c); /* { dg-warning "parentheses" "correct warning" } */
   foo ((a ^ b) - c);
   foo (a ^ (b - c));
@@ -72,7 +72,7 @@  bar (int a, int b, int c)
   foo (1 >= (2 ^ c));
   foo (1 >= 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
   foo ((1 >= 2) ^ 3);
-  foo (1 >= (2 ^ 3));
+  foo (1 >= (0x2 ^ 3));
   foo (a ^ b >= c); /* { dg-warning "parentheses" "correct warning" } */
   foo ((a ^ b) >= c);
   foo (a ^ (b >= c));
@@ -90,7 +90,7 @@  bar (int a, int b, int c)
   foo (1 == (2 ^ c));
   foo (1 == 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
   foo ((1 == 2) ^ 3);
-  foo (1 == (2 ^ 3));
+  foo (1 == (0x2 ^ 3));
   foo (a ^ b == c); /* { dg-warning "parentheses" "correct warning" } */
   foo ((a ^ b) == c);
   foo (a ^ (b == c));
@@ -108,7 +108,7 @@  bar (int a, int b, int c)
   foo (1 < (2 ^ c));
   foo (1 < 2 ^ 3); /* { dg-warning "parentheses" "correct warning" } */
   foo ((1 < 2) ^ 3);
-  foo (1 < (2 ^ 3));
+  foo (1 < (0x2 ^ 3));
   foo (a ^ b < c); /* { dg-warning "parentheses" "correct warning" } */
   foo ((a ^ b) < c);
   foo (a ^ (b < c));
diff --git a/gcc/testsuite/gcc.dg/precedence-1.c b/gcc/testsuite/gcc.dg/precedence-1.c
index f3f1e352708..089cdfc1472 100644
--- a/gcc/testsuite/gcc.dg/precedence-1.c
+++ b/gcc/testsuite/gcc.dg/precedence-1.c
@@ -135,10 +135,10 @@  f (void)
   ASSERT_BIN (0, !=, 2, &, 1, 1, 1, 0);
   ASSERT_BIN (1, &, 2, ==, 0, 0, 1, 0);
   ASSERT_BIN (1, &, 2, !=, 0, 1, 0, 1);
-  ASSERT_BIN (1, &, 2, ^, 3, 3, 3, 1);
+  ASSERT_BIN (1, &, 0x2, ^, 3, 3, 3, 1);
   ASSERT_BIN (3, ^, 2, &, 1, 3, 1, 3);
   ASSERT_BIN (3, ^, 2, |, 1, 1, 1, 0);
-  ASSERT_BIN (3, |, 2, ^, 1, 3, 2, 3);
+  ASSERT_BIN (3, |, 0x2, ^, 1, 3, 2, 3);
   ASSERT_BIN (2, |, 0, &&, 2, 1, 1, 2);
   ASSERT_BIN (2, &&, 0, |, 2, 1, 2, 1);
   ASSERT_BIN (0, &&, 0, ||, 1, 1, 1, 0);
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 3eba6f74b57..f1e7c2b3b2a 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -190,7 +190,7 @@  struct GTY(()) cpp_string {
 #define NAMED_OP	(1 << 4) /* C++ named operators.  */
 #define PREV_FALLTHROUGH (1 << 5) /* On a token preceeded by FALLTHROUGH
 				     comment.  */
-#define BOL		(1 << 6) /* Token at beginning of line.  */
+#define DECIMAL_INT     (1 << 6) /* Decimal integer, set in c-lex.cc.  */
 #define PURE_ZERO	(1 << 7) /* Single 0 digit, used by the C++ frontend,
 				    set in c-lex.cc.  */
 #define SP_DIGRAPH	(1 << 8) /* # or ## token was a digraph.  */
@@ -199,6 +199,7 @@  struct GTY(()) cpp_string {
 				    after a # operator.  */
 #define NO_EXPAND	(1 << 10) /* Do not macro-expand this token.  */
 #define PRAGMA_OP	(1 << 11) /* _Pragma token.  */
+#define BOL		(1 << 12) /* Token at beginning of line.  */
 
 /* Specify which field, if any, of the cpp_token union is used.  */