Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	(revision 187148)
+++ gcc/c-family/c-common.c	(working copy)
@@ -3488,10 +3488,21 @@ expr_original_type (tree expr)
 {
   STRIP_SIGN_NOPS (expr);
   return TREE_TYPE (expr);
 }
 
+/* Given an expression as a c_expr, return its original type.  Do this
+   by stripping any conversion that generates no instruction but don't
+   let the signedness change.  */
+static tree
+c_expr_original_type (struct c_expr expr)
+{
+  if (expr.original_type)
+    return expr.original_type;
+  return expr_original_type (expr.value);
+}
+
 /* Subroutine of build_binary_op, used for comparison operations.
    See if the operands have both been converted from subword integer types
    and, if so, perhaps change them both back to their original type.
    This function is also responsible for converting the two operands
    to the proper common type for comparison.
@@ -3503,16 +3514,16 @@ expr_original_type (tree expr)
    If this function returns nonzero, it means that the comparison has
    a constant value.  What this function returns is an expression for
    that value.  */
 
 tree
-shorten_compare (tree *op0_ptr, tree *op1_ptr, tree *restype_ptr,
+shorten_compare (tree *op0_ptr, struct c_expr *exp_op1, tree *restype_ptr,
 		 enum tree_code *rescode_ptr)
 {
   tree type;
   tree op0 = *op0_ptr;
-  tree op1 = *op1_ptr;
+  tree op1 = exp_op1->value;
   int unsignedp0, unsignedp1;
   int real1, real2;
   tree primop0, primop1;
   enum tree_code code = *rescode_ptr;
   location_t loc = EXPR_LOC_OR_HERE (op0);
@@ -3563,11 +3574,11 @@ shorten_compare (tree *op0_ptr, tree *op
       primop1 = tem;
       tem = op0;
       op0 = op1;
       op1 = tem;
       *op0_ptr = op0;
-      *op1_ptr = op1;
+      exp_op1->value = op1;
       unsignedp0 = unsignedp1;
       unsignedp1 = temi;
       temi = real1;
       real1 = real2;
       real2 = temi;
@@ -3815,11 +3826,13 @@ shorten_compare (tree *op0_ptr, tree *op
 	    warn_type_limits && !in_system_header
 	    && !(TREE_CODE (primop0) == INTEGER_CST
 		 && !TREE_OVERFLOW (convert (c_common_signed_type (type),
 					     primop0)))
 	    /* Do not warn for enumeration types.  */
-	    && (TREE_CODE (expr_original_type (primop0)) != ENUMERAL_TYPE);
+	    && (TREE_CODE (expr_original_type (primop0)) != ENUMERAL_TYPE)
+	    /* Do not warn for enumeration constants. */ 
+	    && (TREE_CODE (c_expr_original_type (*exp_op1)) != ENUMERAL_TYPE);
 	  
 	  switch (code)
 	    {
 	    case GE_EXPR:
 	      if (warn)
@@ -3849,11 +3862,11 @@ shorten_compare (tree *op0_ptr, tree *op
 	    }
 	}
     }
 
   *op0_ptr = convert (type, primop0);
-  *op1_ptr = convert (type, primop1);
+  exp_op1->value = convert (type, primop1);
 
   *restype_ptr = truthvalue_type_node;
 
   return 0;
 }
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	(revision 187148)
+++ gcc/c-family/c-common.h	(working copy)
@@ -523,10 +523,55 @@ struct GTY(()) c_language_function {
   (*(VEC_address (tree, stmt_list_stack)				\
      + VEC_length (tree, stmt_list_stack) - 1))
 
 #define building_stmt_list_p() (!VEC_empty (tree, stmt_list_stack))
 
+/* Record parser information about an expression that is irrelevant
+   for code generation alongside a tree representing its value.  */
+struct c_expr
+{
+  /* The value of the expression.  */
+  tree value;
+  /* Record the original unary/binary operator of an expression, which may
+     have been changed by fold, STRING_CST for unparenthesized string
+     constants, C_MAYBE_CONST_EXPR for __builtin_constant_p calls
+     (even if parenthesized), for subexpressions, and for non-constant
+     initializers, or ERROR_MARK for other expressions (including
+     parenthesized expressions).  */
+  enum tree_code original_code;
+  /* If not NULL, the original type of an expression.  This will
+     differ from the type of the value field for an enum constant.
+     The type of an enum constant is a plain integer type, but this
+     field will be the enum type.  */
+  tree original_type;
+};
+
+/* Type alias for struct c_expr. This allows to use the structure
+   inside the VEC types.  */
+typedef struct c_expr c_expr_t;
+
+static inline struct c_expr c_expr_from_tree (tree t)
+{
+  struct c_expr e;
+  e.value = t;
+  e.original_code = ERROR_MARK;
+  e.original_type = NULL;
+  return e;
+}
+
+/* A varray of c_expr_t.  */
+DEF_VEC_O (c_expr_t);
+DEF_VEC_ALLOC_O (c_expr_t, gc);
+DEF_VEC_ALLOC_O (c_expr_t, heap);
+
+/* Append a new c_expr_t element to V.  */
+#define C_EXPR_APPEND(V, ELEM) \
+  do { \
+    c_expr_t *__elem_p = VEC_safe_push (c_expr_t, gc, V, NULL); \
+    *__elem_p = (ELEM); \
+  } while (0)
+
 /* Language-specific hooks.  */
 
 /* If non-NULL, this function is called after a precompile header file
    is loaded.  */
 extern void (*lang_post_pch_load) (void);
@@ -796,11 +841,11 @@ extern bool keyword_is_decl_specifier (e
 extern tree shorten_binary_op (tree result_type, tree op0, tree op1, bool bitwise);
 
 /* Subroutine of build_binary_op, used for comparison operations.
    See if the operands have both been converted from subword integer types
    and, if so, perhaps change them both back to their original type.  */
-extern tree shorten_compare (tree *, tree *, tree *, enum tree_code *);
+extern tree shorten_compare (tree *, struct c_expr *, tree *, enum tree_code *);
 
 extern tree pointer_int_sum (location_t, enum tree_code, tree, tree);
 
 /* Add qualifiers to a type, in the fashion for C.  */
 extern tree c_build_qualified_type (tree, int);
Index: gcc/testsuite/c-c++-common/pr51712.c
===================================================================
--- gcc/testsuite/c-c++-common/pr51712.c	(revision 187148)
+++ gcc/testsuite/c-c++-common/pr51712.c	(working copy)
@@ -1,18 +1,18 @@
 /* PR c/51712 */
 /* { dg-do compile } */
 /* { dg-options "-Wtype-limits" } */
 
 enum test_enum {
-  FOO,
+  FOO = 0,
   BAR
 };
 
 int valid(enum test_enum arg)
 {
   return arg >= 0 && arg <= BAR;
 }
 
 int valid2(unsigned int arg2)
 {
-  return arg2 >= FOO && arg2 <= BAR; /* { dg-bogus "comparison of unsigned expression" "" { xfail *-*-* } } */
+  return arg2 >= FOO && arg2 <= BAR; /* { dg-bogus "comparison of unsigned expression" "" { xfail c++ } } */
 }
Index: gcc/cp/typeck.c
===================================================================
--- gcc/cp/typeck.c	(revision 187148)
+++ gcc/cp/typeck.c	(working copy)
@@ -4529,17 +4529,18 @@ cp_build_binary_op (location_t location,
 	{
 	  /* Don't write &op0, etc., because that would prevent op0
 	     from being kept in a register.
 	     Instead, make copies of the our local variables and
 	     pass the copies by reference, then copy them back afterward.  */
-	  tree xop0 = op0, xop1 = op1, xresult_type = result_type;
+	  tree xop0 = op0, xresult_type = result_type;
+	  struct c_expr xop1 = c_expr_from_tree (op1);
 	  enum tree_code xresultcode = resultcode;
 	  tree val
 	    = shorten_compare (&xop0, &xop1, &xresult_type, &xresultcode);
 	  if (val != 0)
 	    return cp_convert (boolean_type_node, val);
-	  op0 = xop0, op1 = xop1;
+	  op0 = xop0, op1 = xop1.value;
 	  converted = 1;
 	  resultcode = xresultcode;
 	}
 
       if ((short_compare || code == MIN_EXPR || code == MAX_EXPR)
@@ -4762,12 +4763,13 @@ build_x_unary_op (enum tree_code code, t
 tree
 cp_truthvalue_conversion (tree expr)
 {
   tree type = TREE_TYPE (expr);
   if (TYPE_PTRMEM_P (type))
-    return build_binary_op (EXPR_LOCATION (expr),
-			    NE_EXPR, expr, nullptr_node, 1);
+    return cp_build_binary_op (EXPR_LOCATION (expr),
+			       NE_EXPR, expr, nullptr_node, 
+			       tf_warning_or_error);
   else if (TYPE_PTR_P (type) || TYPE_PTRMEMFUNC_P (type))
     {
       /* With -Wzero-as-null-pointer-constant do not warn for an
 	 'if (p)' or a 'while (!p)', where p is a pointer.  */
       tree ret;
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 187148)
+++ gcc/cp/parser.c	(working copy)
@@ -9537,14 +9537,14 @@ cp_parser_perform_range_for_lookup (tree
     }
   if (TREE_CODE (TREE_TYPE (range)) == ARRAY_TYPE)
     {
       /* If RANGE is an array, we will use pointer arithmetic.  */
       *begin = range;
-      *end = build_binary_op (input_location, PLUS_EXPR,
-			      range,
-			      array_type_nelts_top (TREE_TYPE (range)),
-			      0);
+      *end = cp_build_binary_op (input_location, PLUS_EXPR,
+				 range,
+				 array_type_nelts_top (TREE_TYPE (range)),
+				 tf_warning_or_error);
       return build_pointer_type (TREE_TYPE (TREE_TYPE (range)));
     }
   else
     {
       /* If it is not an array, we must do a bit of magic.  */
Index: gcc/c-tree.h
===================================================================
--- gcc/c-tree.h	(revision 187148)
+++ gcc/c-tree.h	(working copy)
@@ -108,46 +108,10 @@ along with GCC; see the file COPYING3.  
 
 /* For a CONSTRUCTOR, whether some initializer contains a
    subexpression meaning it is not a constant expression.  */
 #define CONSTRUCTOR_NON_CONST(EXPR) TREE_LANG_FLAG_1 (CONSTRUCTOR_CHECK (EXPR))
 
-/* Record parser information about an expression that is irrelevant
-   for code generation alongside a tree representing its value.  */
-struct c_expr
-{
-  /* The value of the expression.  */
-  tree value;
-  /* Record the original unary/binary operator of an expression, which may
-     have been changed by fold, STRING_CST for unparenthesized string
-     constants, C_MAYBE_CONST_EXPR for __builtin_constant_p calls
-     (even if parenthesized), for subexpressions, and for non-constant
-     initializers, or ERROR_MARK for other expressions (including
-     parenthesized expressions).  */
-  enum tree_code original_code;
-  /* If not NULL, the original type of an expression.  This will
-     differ from the type of the value field for an enum constant.
-     The type of an enum constant is a plain integer type, but this
-     field will be the enum type.  */
-  tree original_type;
-};
-
-/* Type alias for struct c_expr. This allows to use the structure
-   inside the VEC types.  */
-typedef struct c_expr c_expr_t;
-
-/* A varray of c_expr_t.  */
-DEF_VEC_O (c_expr_t);
-DEF_VEC_ALLOC_O (c_expr_t, gc);
-DEF_VEC_ALLOC_O (c_expr_t, heap);
-
-/* Append a new c_expr_t element to V.  */
-#define C_EXPR_APPEND(V, ELEM) \
-  do { \
-    c_expr_t *__elem_p = VEC_safe_push (c_expr_t, gc, V, NULL); \
-    *__elem_p = (ELEM); \
-  } while (0)
-
 /* A kind of type specifier.  Note that this information is currently
    only used to distinguish tag definitions, tag references and typeof
    uses.  */
 enum c_typespec_kind {
   /* No typespec.  This appears only in struct c_declspec.  */
@@ -603,10 +567,12 @@ extern tree c_begin_omp_task (void);
 extern tree c_finish_omp_task (location_t, tree, tree);
 extern tree c_finish_omp_clauses (tree);
 extern tree c_build_va_arg (location_t, tree, tree);
 extern tree c_finish_transaction (location_t, tree, int);
 extern tree c_build_vec_perm_expr (location_t, tree, tree, tree);
+extern tree cexp_build_binary_op (location_t, enum tree_code,
+				  struct c_expr, struct c_expr, bool);
 
 /* Set to 0 at beginning of a function definition, set to 1 if
    a return statement that specifies a return value is seen.  */
 
 extern int current_function_returns_value;
Index: gcc/c-typeck.c
===================================================================
--- gcc/c-typeck.c	(revision 187148)
+++ gcc/c-typeck.c	(working copy)
@@ -3320,12 +3320,12 @@ parser_build_binary_op (location_t locat
                 : TREE_TYPE (arg1.value));
   tree type2 = (arg2.original_type
                 ? arg2.original_type
                 : TREE_TYPE (arg2.value));
 
-  result.value = build_binary_op (location, code,
-				  arg1.value, arg2.value, 1);
+  result.value = cexp_build_binary_op (location, code,
+				       arg1, arg2, true);
   result.original_code = code;
   result.original_type = NULL;
 
   if (TREE_CODE (result.value) == ERROR_MARK)
     return result;
@@ -9513,10 +9513,20 @@ scalar_to_vector (location_t loc, enum t
     }
 
   return stv_nothing;
 }
 
+tree
+build_binary_op (location_t loc, enum tree_code code,
+		 tree op0, tree op1, int convert_p)
+{
+  return cexp_build_binary_op (loc, code,
+			       c_expr_from_tree (op0),
+			       c_expr_from_tree (op1),
+			       convert_p);
+}
+
 /* Build a binary-operation expression without default conversions.
    CODE is the kind of expression to build.
    LOCATION is the operator's location.
    This function differs from `build' in several ways:
    the data type of the result is computed and recorded in it,
@@ -9530,17 +9540,17 @@ scalar_to_vector (location_t loc, enum t
    or array types, because either they will have the default conversions
    performed or they have both just been converted to some other type in which
    the arithmetic is to be done.  */
 
 tree
-build_binary_op (location_t location, enum tree_code code,
-		 tree orig_op0, tree orig_op1, int convert_p)
+cexp_build_binary_op (location_t location, enum tree_code code,
+		      struct c_expr expr_op0, struct c_expr expr_op1, bool convert_p)
 {
   tree type0, type1, orig_type0, orig_type1;
   tree eptype;
   enum tree_code code0, code1;
-  tree op0, op1;
+  tree op0, op1, orig_op0, orig_op1;
   tree ret = error_mark_node;
   const char *invalid_op_diag;
   bool op0_int_operands, op1_int_operands;
   bool int_const, int_const_or_overflow, int_operands;
 
@@ -9602,12 +9612,13 @@ build_binary_op (location_t location, en
   bool boolean_op = false;
 
   if (location == UNKNOWN_LOCATION)
     location = input_location;
 
-  op0 = orig_op0;
-  op1 = orig_op1;
+
+  op0 = orig_op0 = expr_op0.value;
+  op1 = orig_op1 = expr_op1.value;
 
   op0_int_operands = EXPR_INT_CONST_OPERANDS (orig_op0);
   if (op0_int_operands)
     op0 = remove_c_maybe_const_expr (op0);
   op1_int_operands = EXPR_INT_CONST_OPERANDS (orig_op1);
@@ -10423,22 +10434,23 @@ build_binary_op (location_t location, en
 	{
 	  /* Don't write &op0, etc., because that would prevent op0
 	     from being kept in a register.
 	     Instead, make copies of the our local variables and
 	     pass the copies by reference, then copy them back afterward.  */
-	  tree xop0 = op0, xop1 = op1, xresult_type = result_type;
+	  tree xop0 = op0, xresult_type = result_type;
+	  struct c_expr xop1 = expr_op1;
+	  xop1.value = op1;
 	  enum tree_code xresultcode = resultcode;
 	  tree val
 	    = shorten_compare (&xop0, &xop1, &xresult_type, &xresultcode);
-
 	  if (val != 0)
 	    {
 	      ret = val;
 	      goto return_build_binary_op;
 	    }
 
-	  op0 = xop0, op1 = xop1;
+	  op0 = xop0, op1 = xop1.value;
 	  converted = 1;
 	  resultcode = xresultcode;
 
 	  if (c_inhibit_evaluation_warnings == 0)
 	    {
