diff mbox

[C/C++] Implement -Wshift-negative-value (PR c/65179)

Message ID 20150506113758.GV3384@redhat.com
State New
Headers show

Commit Message

Marek Polacek May 6, 2015, 11:37 a.m. UTC
On Wed, Apr 29, 2015 at 10:54:58PM +0000, Joseph Myers wrote:
> On Mon, 27 Apr 2015, Marek Polacek wrote:
> 
> > trigger by default.  One change is that we reject programs that use shift with
> > undefined behavior in a context where a constant expression is required, thus
> > e.g. enum E { A = -1 << 0 };
> > But I hope that's reasonable.
> 
> That seems appropriate (for C99 and above).
> 
> But if someone explicitly uses -Wshift-negative-value, I'd expect that to 
> produce the warnings (as opposed to the rejections where a constant 
> expression is required) even in C90 mode.  That is, for the warnings, I 
> think flag_isoc99 should maybe affect the default (whether -Wextra enables 
> the warning, or whatever such approach gets taken), but not whether 
> -Wshift-negative-value, given that the option has been enabled, produces 
> warnings.

Ah, indeed.  The following patch hopefully addresses those defects.  The
tests show when the warning triggers and when not as well as when we reject
invalid shifts and when not.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2015-05-06  Marek Polacek  <polacek@redhat.com>

	PR c/65179
	* c-common.c (c_fully_fold_internal): Warn when left shifting a
	negative value.
	* c.opt (Wshift-negative-value): New option.
	* c-opts.c (c_common_post_options): Set warn_shift_negative_value
	when -Wextra and C99/C++11 mode.

	* c-typeck.c (build_binary_op): Warn when left shifting a negative
	value.

	* typeck.c (cp_build_binary_op): Warn when left shifting a negative
	value.

	* doc/invoke.texi: Document -Wshift-negative-value.

	* c-c++-common/Wshift-negative-value-1.c: New test.
	* testsuite/c-c++-common/Wshift-negative-value-2.c: New test.
	* testsuite/c-c++-common/Wshift-negative-value-3.c: New test.
	* testsuite/c-c++-common/Wshift-negative-value-4.c: New test.
	* testsuite/c-c++-common/Wshift-negative-value-5.c: New test.
	* testsuite/c-c++-common/Wshift-negative-value-6.c: New test.
	* testsuite/gcc.dg/c90-left-shift-1.c: New test.
	* testsuite/gcc.dg/c99-const-expr-7.c: Add dg-error.
	* testsuite/gcc.dg/c99-left-shift-1.c: New test.


	Marek

Comments

Jeff Law May 7, 2015, 6 p.m. UTC | #1
On 05/06/2015 05:37 AM, Marek Polacek wrote:
> On Wed, Apr 29, 2015 at 10:54:58PM +0000, Joseph Myers wrote:
>> On Mon, 27 Apr 2015, Marek Polacek wrote:
>>
>>> trigger by default.  One change is that we reject programs that use shift with
>>> undefined behavior in a context where a constant expression is required, thus
>>> e.g. enum E { A = -1 << 0 };
>>> But I hope that's reasonable.
>>
>> That seems appropriate (for C99 and above).
>>
>> But if someone explicitly uses -Wshift-negative-value, I'd expect that to
>> produce the warnings (as opposed to the rejections where a constant
>> expression is required) even in C90 mode.  That is, for the warnings, I
>> think flag_isoc99 should maybe affect the default (whether -Wextra enables
>> the warning, or whatever such approach gets taken), but not whether
>> -Wshift-negative-value, given that the option has been enabled, produces
>> warnings.
>
> Ah, indeed.  The following patch hopefully addresses those defects.  The
> tests show when the warning triggers and when not as well as when we reject
> invalid shifts and when not.
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2015-05-06  Marek Polacek  <polacek@redhat.com>
>
> 	PR c/65179
> 	* c-common.c (c_fully_fold_internal): Warn when left shifting a
> 	negative value.
> 	* c.opt (Wshift-negative-value): New option.
> 	* c-opts.c (c_common_post_options): Set warn_shift_negative_value
> 	when -Wextra and C99/C++11 mode.
>
> 	* c-typeck.c (build_binary_op): Warn when left shifting a negative
> 	value.
>
> 	* typeck.c (cp_build_binary_op): Warn when left shifting a negative
> 	value.
>
> 	* doc/invoke.texi: Document -Wshift-negative-value.
>
> 	* c-c++-common/Wshift-negative-value-1.c: New test.
> 	* testsuite/c-c++-common/Wshift-negative-value-2.c: New test.
> 	* testsuite/c-c++-common/Wshift-negative-value-3.c: New test.
> 	* testsuite/c-c++-common/Wshift-negative-value-4.c: New test.
> 	* testsuite/c-c++-common/Wshift-negative-value-5.c: New test.
> 	* testsuite/c-c++-common/Wshift-negative-value-6.c: New test.
> 	* testsuite/gcc.dg/c90-left-shift-1.c: New test.
> 	* testsuite/gcc.dg/c99-const-expr-7.c: Add dg-error.
> 	* testsuite/gcc.dg/c99-left-shift-1.c: New test.
OK.  Please install if you haven't already.

jeff
Marek Polacek May 7, 2015, 7:15 p.m. UTC | #2
On Thu, May 07, 2015 at 12:00:20PM -0600, Jeff Law wrote:
> OK.  Please install if you haven't already.

I have not, so will do momentarily.  Thanks,

	Marek
Steve Ellcey May 8, 2015, 4:38 p.m. UTC | #3
On Thu, 2015-05-07 at 21:15 +0200, Marek Polacek wrote:
> On Thu, May 07, 2015 at 12:00:20PM -0600, Jeff Law wrote:
> > OK.  Please install if you haven't already.
> 
> I have not, so will do momentarily.  Thanks,
> 
> 	Marek

Marek,

This patch has broken the glibc build.  I am not sure if the problem is
a bug in your patch or a bug in the code used by glibc.  Here is a
cutdown test case from glibc (timezone/scheck.c).  This code compiled
before your patch but now it fails with:

x.c:4:3: error: initializer element is not constant
   ((((time_t) -1) < 0)



__extension__ typedef long int __time_t;
typedef __time_t time_t;
static time_t const time_t_min =
  ((((time_t) -1) < 0)
   ? (time_t) -1 << (8 * sizeof (time_t) - 1)
   : 0)



Steve Ellcey
sellcey@imgtec.com
Markus Trippelsdorf May 8, 2015, 4:40 p.m. UTC | #4
On 2015.05.08 at 09:38 -0700, Steve Ellcey wrote:
> 
> This patch has broken the glibc build.  I am not sure if the problem is
> a bug in your patch or a bug in the code used by glibc.  Here is a
> cutdown test case from glibc (timezone/scheck.c).  This code compiled
> before your patch but now it fails with:
> 
> x.c:4:3: error: initializer element is not constant
>    ((((time_t) -1) < 0)
> 
> 
> 
> __extension__ typedef long int __time_t;
> typedef __time_t time_t;
> static time_t const time_t_min =
>   ((((time_t) -1) < 0)
>    ? (time_t) -1 << (8 * sizeof (time_t) - 1)
>    : 0)
> 

See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66066
Joseph Myers May 8, 2015, 4:59 p.m. UTC | #5
On Fri, 8 May 2015, Steve Ellcey wrote:

> On Thu, 2015-05-07 at 21:15 +0200, Marek Polacek wrote:
> > On Thu, May 07, 2015 at 12:00:20PM -0600, Jeff Law wrote:
> > > OK.  Please install if you haven't already.
> > 
> > I have not, so will do momentarily.  Thanks,
> > 
> > 	Marek
> 
> Marek,
> 
> This patch has broken the glibc build.  I am not sure if the problem is
> a bug in your patch or a bug in the code used by glibc.  Here is a
> cutdown test case from glibc (timezone/scheck.c).  This code compiled
> before your patch but now it fails with:
> 
> x.c:4:3: error: initializer element is not constant
>    ((((time_t) -1) < 0)
> 
> 
> 
> __extension__ typedef long int __time_t;
> typedef __time_t time_t;
> static time_t const time_t_min =
>   ((((time_t) -1) < 0)
>    ? (time_t) -1 << (8 * sizeof (time_t) - 1)
>    : 0)

Paul, although glibc's copy of parts of tzcode is a bit out of date, it 
looks like the current https://github.com/eggert/tz.git still has the 
problematic code in private.h, relying on left-shifting -1 which has 
undefined behavior in C99/C11 (implementation-defined in C90, as per 
DR#081).
diff mbox

Patch

diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index ada8e8a..378f237 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -1361,6 +1361,14 @@  c_fully_fold_internal (tree expr, bool in_init, bool *maybe_const_operands,
 	  && !TREE_OVERFLOW_P (op0)
 	  && !TREE_OVERFLOW_P (op1))
 	overflow_warning (EXPR_LOCATION (expr), ret);
+      if (code == LSHIFT_EXPR
+	  && TREE_CODE (orig_op0) != INTEGER_CST
+	  && TREE_CODE (TREE_TYPE (orig_op0)) == INTEGER_TYPE
+	  && TREE_CODE (op0) == INTEGER_CST
+	  && c_inhibit_evaluation_warnings == 0
+	  && tree_int_cst_sgn (op0) < 0)
+	warning_at (loc, OPT_Wshift_negative_value,
+		    "left shift of negative value");
       if ((code == LSHIFT_EXPR || code == RSHIFT_EXPR)
 	  && TREE_CODE (orig_op1) != INTEGER_CST
 	  && TREE_CODE (op1) == INTEGER_CST
diff --git gcc/c-family/c-opts.c gcc/c-family/c-opts.c
index 1a67b5a..a61d6a8 100644
--- gcc/c-family/c-opts.c
+++ gcc/c-family/c-opts.c
@@ -866,6 +866,11 @@  c_common_post_options (const char **pfilename)
   if (warn_implicit_int == -1)
     warn_implicit_int = flag_isoc99;
 
+  /* -Wshift-negative-value is enabled by -Wextra in C99 and C++11 modes.  */
+  if (warn_shift_negative_value == -1)
+    warn_shift_negative_value = (extra_warnings
+				 && (cxx_dialect >= cxx11 || flag_isoc99));
+
   /* Declone C++ 'structors if -Os.  */
   if (flag_declone_ctor_dtor == -1)
     flag_declone_ctor_dtor = optimize_size;
diff --git gcc/c-family/c.opt gcc/c-family/c.opt
index 8ef0cea..48947b4 100644
--- gcc/c-family/c.opt
+++ gcc/c-family/c.opt
@@ -781,6 +781,10 @@  Wshift-count-overflow
 C ObjC C++ ObjC++ Var(warn_shift_count_overflow) Init(1) Warning
 Warn if shift count >= width of type
 
+Wshift-negative-value
+C ObjC C++ ObjC++ Var(warn_shift_negative_value) Init(-1) Warning
+Warn if left shifting a negative value
+
 Wsign-compare
 C ObjC C++ ObjC++ Var(warn_sign_compare) Warning LangEnabledBy(C++ ObjC++,Wall)
 Warn about signed-unsigned comparisons
diff --git gcc/c/c-typeck.c gcc/c/c-typeck.c
index 328f294..73275aa 100644
--- gcc/c/c-typeck.c
+++ gcc/c/c-typeck.c
@@ -10697,6 +10697,17 @@  build_binary_op (location_t location, enum tree_code code,
 	  && code1 == INTEGER_TYPE)
 	{
 	  doing_shift = true;
+	  if (TREE_CODE (op0) == INTEGER_CST
+	      && tree_int_cst_sgn (op0) < 0)
+	    {
+	      /* Don't reject a left shift of a negative value in a context
+		 where a constant expression is needed in C90.  */
+	      if (flag_isoc99)
+		int_const = false;
+	      if (c_inhibit_evaluation_warnings == 0)
+		warning_at (location, OPT_Wshift_negative_value,
+			    "left shift of negative value");
+	    }
 	  if (TREE_CODE (op1) == INTEGER_CST)
 	    {
 	      if (tree_int_cst_sgn (op1) < 0)
diff --git gcc/cp/typeck.c gcc/cp/typeck.c
index 91db32a..549e4b1 100644
--- gcc/cp/typeck.c
+++ gcc/cp/typeck.c
@@ -4326,11 +4326,20 @@  cp_build_binary_op (location_t location,
 	}
       else if (code0 == INTEGER_TYPE && code1 == INTEGER_TYPE)
 	{
+	  tree const_op0 = fold_non_dependent_expr (op0);
+	  if (TREE_CODE (const_op0) != INTEGER_CST)
+	    const_op0 = op0;
 	  tree const_op1 = fold_non_dependent_expr (op1);
 	  if (TREE_CODE (const_op1) != INTEGER_CST)
 	    const_op1 = op1;
 	  result_type = type0;
 	  doing_shift = true;
+	  if (TREE_CODE (const_op0) == INTEGER_CST
+	      && tree_int_cst_sgn (const_op0) < 0
+	      && (complain & tf_warning)
+	      && c_inhibit_evaluation_warnings == 0)
+	    warning (OPT_Wshift_negative_value,
+		     "left shift of negative value");
 	  if (TREE_CODE (const_op1) == INTEGER_CST)
 	    {
 	      if (tree_int_cst_lt (const_op1, integer_zero_node))
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index 9c8aa99..30f8ac7 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -271,7 +271,7 @@  Objective-C and Objective-C++ Dialects}.
 -Wpointer-arith  -Wno-pointer-to-int-cast @gol
 -Wredundant-decls  -Wno-return-local-addr @gol
 -Wreturn-type  -Wsequence-point  -Wshadow  -Wno-shadow-ivar @gol
--Wshift-count-negative -Wshift-count-overflow @gol
+-Wshift-count-negative -Wshift-count-overflow -Wshift-negative-value @gol
 -Wsign-compare  -Wsign-conversion -Wfloat-conversion @gol
 -Wsizeof-pointer-memaccess  -Wsizeof-array-argument @gol
 -Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol
@@ -3489,6 +3489,7 @@  name is still supported, but the newer name is more descriptive.)
 -Wsign-compare  @gol
 -Wtype-limits  @gol
 -Wuninitialized  @gol
+-Wshift-negative-value  @gol
 -Wunused-parameter @r{(only with} @option{-Wunused} @r{or} @option{-Wall}@r{)} @gol
 -Wunused-but-set-parameter @r{(only with} @option{-Wunused} @r{or} @option{-Wall}@r{)}  @gol
 }
@@ -3922,6 +3923,12 @@  Warn if shift count is negative. This warning is enabled by default.
 @opindex Wno-shift-count-overflow
 Warn if shift count >= width of type. This warning is enabled by default.
 
+@item -Wshift-negative-value
+@opindex Wshift-negative-value
+@opindex Wno-shift-negative-value
+Warn if left shifting a negative value.  This warning is enabled by
+@option{-Wextra} in C99 and C++11 modes (and newer).
+
 @item -Wswitch
 @opindex Wswitch
 @opindex Wno-switch
diff --git gcc/testsuite/c-c++-common/Wshift-negative-value-1.c gcc/testsuite/c-c++-common/Wshift-negative-value-1.c
index e69de29..5d803ad 100644
--- gcc/testsuite/c-c++-common/Wshift-negative-value-1.c
+++ gcc/testsuite/c-c++-common/Wshift-negative-value-1.c
@@ -0,0 +1,49 @@ 
+/* PR c/65179 */
+/* { dg-do compile } */
+/* { dg-options "-O -Wextra" } */
+/* { dg-additional-options "-std=c++11" { target c++ } } */
+
+enum E {
+  A = 0 << 1,
+  B = 1 << 1,
+  C = -1 << 1, /* { dg-warning "left shift of negative value|not an integer constant" } */
+  D = 0 >> 1,
+  E = 1 >> 1,
+  F = -1 >> 1
+};
+
+int
+left (int x)
+{
+  /* Warn for LSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z << x;
+  r += o << x;
+  r += m << x; /* { dg-warning "left shift of negative value" } */
+  r += 0 << x;
+  r += 1 << x;
+  r += -1 << x; /* { dg-warning "left shift of negative value" } */
+  r += -1U << x;
+  return r;
+}
+
+int
+right (int x)
+{
+  /* Shouldn't warn for RSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z >> x;
+  r += o >> x;
+  r += m >> x;
+  r += 0 >> x;
+  r += 1 >> x;
+  r += -1 >> x;
+  r += -1U >> x;
+  return r;
+}
diff --git gcc/testsuite/c-c++-common/Wshift-negative-value-2.c gcc/testsuite/c-c++-common/Wshift-negative-value-2.c
index e69de29..fc89af1 100644
--- gcc/testsuite/c-c++-common/Wshift-negative-value-2.c
+++ gcc/testsuite/c-c++-common/Wshift-negative-value-2.c
@@ -0,0 +1,49 @@ 
+/* PR c/65179 */
+/* { dg-do compile } */
+/* { dg-options "-O -Wshift-negative-value" } */
+/* { dg-additional-options "-std=c++11" { target c++ } } */
+
+enum E {
+  A = 0 << 1,
+  B = 1 << 1,
+  C = -1 << 1, /* { dg-warning "left shift of negative value" } */
+  D = 0 >> 1,
+  E = 1 >> 1,
+  F = -1 >> 1
+};
+
+int
+left (int x)
+{
+  /* Warn for LSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z << x;
+  r += o << x;
+  r += m << x; /* { dg-warning "left shift of negative value" } */
+  r += 0 << x;
+  r += 1 << x;
+  r += -1 << x; /* { dg-warning "left shift of negative value" } */
+  r += -1U << x;
+  return r;
+}
+
+int
+right (int x)
+{
+  /* Shouldn't warn for RSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z >> x;
+  r += o >> x;
+  r += m >> x;
+  r += 0 >> x;
+  r += 1 >> x;
+  r += -1 >> x;
+  r += -1U >> x;
+  return r;
+}
diff --git gcc/testsuite/c-c++-common/Wshift-negative-value-3.c gcc/testsuite/c-c++-common/Wshift-negative-value-3.c
index e69de29..bf9b1a0 100644
--- gcc/testsuite/c-c++-common/Wshift-negative-value-3.c
+++ gcc/testsuite/c-c++-common/Wshift-negative-value-3.c
@@ -0,0 +1,49 @@ 
+/* PR c/65179 */
+/* { dg-do compile } */
+/* { dg-options "-O -Wextra -Wno-shift-negative-value" } */
+/* { dg-additional-options "-std=c++11" { target c++ } } */
+
+enum E {
+  A = 0 << 1,
+  B = 1 << 1,
+  C = -1 << 1,
+  D = 0 >> 1,
+  E = 1 >> 1,
+  F = -1 >> 1
+};
+
+int
+left (int x)
+{
+  /* Warn for LSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z << x;
+  r += o << x;
+  r += m << x; /* { dg-bogus "left shift of negative value" } */
+  r += 0 << x;
+  r += 1 << x;
+  r += -1 << x; /* { dg-bogus "left shift of negative value" } */
+  r += -1U << x;
+  return r;
+}
+
+int
+right (int x)
+{
+  /* Shouldn't warn for RSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z >> x;
+  r += o >> x;
+  r += m >> x;
+  r += 0 >> x;
+  r += 1 >> x;
+  r += -1 >> x;
+  r += -1U >> x;
+  return r;
+}
diff --git gcc/testsuite/c-c++-common/Wshift-negative-value-4.c gcc/testsuite/c-c++-common/Wshift-negative-value-4.c
index e69de29..85fbd0e 100644
--- gcc/testsuite/c-c++-common/Wshift-negative-value-4.c
+++ gcc/testsuite/c-c++-common/Wshift-negative-value-4.c
@@ -0,0 +1,49 @@ 
+/* PR c/65179 */
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* { dg-additional-options "-std=c++11" { target c++ } } */
+
+enum E {
+  A = 0 << 1,
+  B = 1 << 1,
+  C = -1 << 1,
+  D = 0 >> 1,
+  E = 1 >> 1,
+  F = -1 >> 1
+};
+
+int
+left (int x)
+{
+  /* Warn for LSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z << x;
+  r += o << x;
+  r += m << x; /* { dg-bogus "left shift of negative value" } */
+  r += 0 << x;
+  r += 1 << x;
+  r += -1 << x; /* { dg-bogus "left shift of negative value" } */
+  r += -1U << x;
+  return r;
+}
+
+int
+right (int x)
+{
+  /* Shouldn't warn for RSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z >> x;
+  r += o >> x;
+  r += m >> x;
+  r += 0 >> x;
+  r += 1 >> x;
+  r += -1 >> x;
+  r += -1U >> x;
+  return r;
+}
diff --git gcc/testsuite/c-c++-common/Wshift-negative-value-5.c gcc/testsuite/c-c++-common/Wshift-negative-value-5.c
index e69de29..74ecd1e 100644
--- gcc/testsuite/c-c++-common/Wshift-negative-value-5.c
+++ gcc/testsuite/c-c++-common/Wshift-negative-value-5.c
@@ -0,0 +1,50 @@ 
+/* PR c/65179 */
+/* { dg-do compile } */
+/* { dg-options "-O -Wshift-negative-value" } */
+/* { dg-additional-options "-std=c++03" { target c++ } } */
+/* { dg-additional-options "-std=c90" { target c } } */
+
+enum E {
+  A = 0 << 1,
+  B = 1 << 1,
+  C = -1 << 1, /* { dg-warning "left shift of negative value" } */
+  D = 0 >> 1,
+  E = 1 >> 1,
+  F = -1 >> 1
+};
+
+int
+left (int x)
+{
+  /* Warn for LSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z << x;
+  r += o << x;
+  r += m << x; /* { dg-warning "left shift of negative value" } */
+  r += 0 << x;
+  r += 1 << x;
+  r += -1 << x; /* { dg-warning "left shift of negative value" } */
+  r += -1U << x;
+  return r;
+}
+
+int
+right (int x)
+{
+  /* Shouldn't warn for RSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z >> x;
+  r += o >> x;
+  r += m >> x;
+  r += 0 >> x;
+  r += 1 >> x;
+  r += -1 >> x;
+  r += -1U >> x;
+  return r;
+}
diff --git gcc/testsuite/c-c++-common/Wshift-negative-value-6.c gcc/testsuite/c-c++-common/Wshift-negative-value-6.c
index e69de29..de9db52 100644
--- gcc/testsuite/c-c++-common/Wshift-negative-value-6.c
+++ gcc/testsuite/c-c++-common/Wshift-negative-value-6.c
@@ -0,0 +1,50 @@ 
+/* PR c/65179 */
+/* { dg-do compile } */
+/* { dg-options "-O -Wextra" } */
+/* { dg-additional-options "-std=c++03" { target c++ } } */
+/* { dg-additional-options "-std=c90" { target c } } */
+
+enum E {
+  A = 0 << 1,
+  B = 1 << 1,
+  C = -1 << 1, /* { dg-bogus "left shift of negative value" } */
+  D = 0 >> 1,
+  E = 1 >> 1,
+  F = -1 >> 1
+};
+
+int
+left (int x)
+{
+  /* Warn for LSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z << x;
+  r += o << x;
+  r += m << x; /* { dg-bogus "left shift of negative value" } */
+  r += 0 << x;
+  r += 1 << x;
+  r += -1 << x; /* { dg-bogus "left shift of negative value" } */
+  r += -1U << x;
+  return r;
+}
+
+int
+right (int x)
+{
+  /* Shouldn't warn for RSHIFT_EXPR.  */
+  const int z = 0;
+  const int o = 1;
+  const int m = -1;
+  int r = 0;
+  r += z >> x;
+  r += o >> x;
+  r += m >> x;
+  r += 0 >> x;
+  r += 1 >> x;
+  r += -1 >> x;
+  r += -1U >> x;
+  return r;
+}
diff --git gcc/testsuite/gcc.dg/c90-left-shift-1.c gcc/testsuite/gcc.dg/c90-left-shift-1.c
index e69de29..755595f 100644
--- gcc/testsuite/gcc.dg/c90-left-shift-1.c
+++ gcc/testsuite/gcc.dg/c90-left-shift-1.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1990 -pedantic-errors" } */
+
+enum E { A = -2 << 1 };
+int i = -1 << 0;
+
+int
+f (int i)
+{
+  switch (i)
+  case -1 << 0: break;
+}
diff --git gcc/testsuite/gcc.dg/c99-const-expr-7.c gcc/testsuite/gcc.dg/c99-const-expr-7.c
index b872077..75b0567 100644
--- gcc/testsuite/gcc.dg/c99-const-expr-7.c
+++ gcc/testsuite/gcc.dg/c99-const-expr-7.c
@@ -30,8 +30,8 @@  int f1 = (0 ? 0 << -1 : 0);
 int g1 = (0 ? 0 >> 1000 : 0);
 int h1 = (0 ? 0 >> -1: 0);
 
-/* Allowed for now, but actually undefined behavior in C99.  */
 int i = -1 << 0;
+/* { dg-error "constant" "constant" { target *-*-* } 33 } */
 
 int j[1] = { DBL_MAX }; /* { dg-warning "overflow in implicit constant conversion" } */
 /* { dg-error "overflow in constant expression" "constant" { target *-*-* } 36 } */
diff --git gcc/testsuite/gcc.dg/c99-left-shift-1.c gcc/testsuite/gcc.dg/c99-left-shift-1.c
index e69de29..9a73049 100644
--- gcc/testsuite/gcc.dg/c99-left-shift-1.c
+++ gcc/testsuite/gcc.dg/c99-left-shift-1.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1999 -pedantic-errors" } */
+
+enum E { A = -2 << 1 }; /* { dg-error "constant expression" } */
+int i = -1 << 0; /* { dg-error "constant expression" } */
+
+int
+f (int i)
+{
+  switch (i)
+  case -1 << 0: break; /* { dg-error "constant expression" } */
+}