This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [Bug c++/19199] [3.3/3.4/4.0/4.1 Regression] Wrong warning about returning a reference to a temporary


On Mar  8, 2005, Roger Sayle <roger@eyesopen.com> wrote:

> On 8 Mar 2005, Alexandre Oliva wrote:
>> 
>> * fold-const.c (non_lvalue): Split tests into...
>> (maybe_lvalue_p): New function.
>> (fold_ternary): Use it to avoid turning a COND_EXPR lvalue into
>> a MIN_EXPR rvalue.

> This version is Ok for mainline, and currently open release branches.

Unfortunately, the problem in g++.oliva/expr2.C resurfaced, because,
as it turns out, the transformation (I != J ? I : J) => I yields an
lvalue as required, but not the *correct* lvalue in all cases.  I
tried what you'd suggested on IRC (testing whether the thing is an
lvalue after the fact), but that obviously failed in this case as
well.

So I figured a better approach would be to perform lvalue tests to
fold_cond_expr_with_comparison(), where we can avoid specific
inappropriate transformations while still trying other alternatives.

While at that, I figured we could use pedantic_lvalues to avoid
refraining from applying optimizations just because it looks like the
cond-expr might be an lvalue, even though the language requires it not
to be.

This is currently bootstrapping on x86_64-linux-gnu.  Ok to install if
bootstrap completes and regtesting passes?

Index: gcc/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	PR c++/19199
	* fold-const.c (non_lvalue): Split tests into...
	(maybe_lvalue_p): New function.
	(fold_cond_expr_with_comparison): Preserve lvalue-ness.

Index: gcc/fold-const.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/fold-const.c,v
retrieving revision 1.535
diff -u -p -r1.535 fold-const.c
--- gcc/fold-const.c 7 Mar 2005 21:24:21 -0000 1.535
+++ gcc/fold-const.c 9 Mar 2005 03:59:18 -0000
@@ -2005,16 +2005,12 @@ fold_convert (tree type, tree arg)
     }
 }
 
-/* Return an expr equal to X but certainly not valid as an lvalue.  */
+/* Return false if expr can be assumed to not be an lvalue, true
+   otherwise.  */
 
-tree
-non_lvalue (tree x)
+static bool
+maybe_lvalue_p (tree x)
 {
-  /* While we are in GIMPLE, NON_LVALUE_EXPR doesn't mean anything to
-     us.  */
-  if (in_gimple_form)
-    return x;
-
   /* We only need to wrap lvalue tree codes.  */
   switch (TREE_CODE (x))
   {
@@ -2054,8 +2050,24 @@ non_lvalue (tree x)
     /* Assume the worst for front-end tree codes.  */
     if ((int)TREE_CODE (x) >= NUM_TREE_CODES)
       break;
-    return x;
+    return false;
   }
+
+  return true;
+}
+
+/* Return an expr equal to X but certainly not valid as an lvalue.  */
+
+tree
+non_lvalue (tree x)
+{
+  /* While we are in GIMPLE, NON_LVALUE_EXPR doesn't mean anything to
+     us.  */
+  if (in_gimple_form)
+    return x;
+
+  if (! maybe_lvalue_p (x))
+    return x;
   return build1 (NON_LVALUE_EXPR, TREE_TYPE (x), x);
 }
 
@@ -4162,7 +4174,15 @@ fold_cond_expr_with_comparison (tree typ
   tree arg00 = TREE_OPERAND (arg0, 0);
   tree arg01 = TREE_OPERAND (arg0, 1);
   tree arg1_type = TREE_TYPE (arg1);
-  tree tem;
+  tree tem = NULL;
+  /* If the COND_EXPR can possibly be an lvalue, we don't want to
+     perform transformations that return a simplified result that will
+     be recognized as lvalue, but that will not match the expected
+     result.  We may still return other expressions that would be
+     incorrect, but those are going to be rvalues, and the caller is
+     supposed to discard them.  */
+  bool lvalue = !pedantic_lvalues
+    && maybe_lvalue_p (arg1) && maybe_lvalue_p (arg2);
 
   STRIP_NOPS (arg1);
   STRIP_NOPS (arg2);
@@ -4196,10 +4216,12 @@ fold_cond_expr_with_comparison (tree typ
       case EQ_EXPR:
       case UNEQ_EXPR:
 	tem = fold_convert (arg1_type, arg1);
-	return pedantic_non_lvalue (fold_convert (type, negate_expr (tem)));
+	tem = pedantic_non_lvalue (fold_convert (type, negate_expr (tem)));
+	break;
       case NE_EXPR:
       case LTGT_EXPR:
-	return pedantic_non_lvalue (fold_convert (type, arg1));
+	tem = pedantic_non_lvalue (fold_convert (type, arg1));
+	break;
       case UNGE_EXPR:
       case UNGT_EXPR:
 	if (flag_trapping_math)
@@ -4211,7 +4233,8 @@ fold_cond_expr_with_comparison (tree typ
 	  arg1 = fold_convert (lang_hooks.types.signed_type
 			       (TREE_TYPE (arg1)), arg1);
 	tem = fold (build1 (ABS_EXPR, TREE_TYPE (arg1), arg1));
-	return pedantic_non_lvalue (fold_convert (type, tem));
+	tem = pedantic_non_lvalue (fold_convert (type, tem));
+	break;
       case UNLE_EXPR:
       case UNLT_EXPR:
 	if (flag_trapping_math)
@@ -4222,12 +4245,18 @@ fold_cond_expr_with_comparison (tree typ
 	  arg1 = fold_convert (lang_hooks.types.signed_type
 			       (TREE_TYPE (arg1)), arg1);
 	tem = fold (build1 (ABS_EXPR, TREE_TYPE (arg1), arg1));
-	return negate_expr (fold_convert (type, tem));
+	tem = negate_expr (fold_convert (type, tem));
+	break;
       default:
 	gcc_assert (TREE_CODE_CLASS (comp_code) == tcc_comparison);
 	break;
       }
 
+  if (tem && (! lvalue || maybe_lvalue_p (tem)))
+    return tem;
+  else
+    tem = NULL;
+
   /* A != 0 ? A : 0 is simply A, unless A is -0.  Likewise
      A == 0 ? A : 0 is always 0 unless A is -0.  Note that
      both transformations are correct when A is NaN: A != 0
@@ -4236,11 +4265,16 @@ fold_cond_expr_with_comparison (tree typ
   if (integer_zerop (arg01) && integer_zerop (arg2))
     {
       if (comp_code == NE_EXPR)
-	return pedantic_non_lvalue (fold_convert (type, arg1));
+	tem = pedantic_non_lvalue (fold_convert (type, arg1));
       else if (comp_code == EQ_EXPR)
-	return fold_convert (type, integer_zero_node);
+	tem = fold_convert (type, integer_zero_node);
     }
 
+  if (tem && (! lvalue || maybe_lvalue_p (tem)))
+    return tem;
+  else
+    tem = NULL;
+
   /* Try some transformations of A op B ? A : B.
 
      A == B? A : B    same as B
@@ -4284,9 +4318,15 @@ fold_cond_expr_with_comparison (tree typ
       switch (comp_code)
 	{
 	case EQ_EXPR:
-	  return pedantic_non_lvalue (fold_convert (type, arg2));
+	  if (lvalue)
+	    break;
+	  tem = pedantic_non_lvalue (fold_convert (type, arg2));
+	  break;
 	case NE_EXPR:
-	  return pedantic_non_lvalue (fold_convert (type, arg1));
+	  if (lvalue)
+	    break;
+	  tem = pedantic_non_lvalue (fold_convert (type, arg1));
+	  break;
 	case LE_EXPR:
 	case LT_EXPR:
 	case UNLE_EXPR:
@@ -4302,7 +4342,7 @@ fold_cond_expr_with_comparison (tree typ
 	      tem = (comp_code == LE_EXPR || comp_code == UNLE_EXPR)
 		    ? fold (build2 (MIN_EXPR, comp_type, comp_op0, comp_op1))
 		    : fold (build2 (MIN_EXPR, comp_type, comp_op1, comp_op0));
-	      return pedantic_non_lvalue (fold_convert (type, tem));
+	      tem = pedantic_non_lvalue (fold_convert (type, tem));
 	    }
 	  break;
 	case GE_EXPR:
@@ -4316,16 +4356,16 @@ fold_cond_expr_with_comparison (tree typ
 	      tem = (comp_code == GE_EXPR || comp_code == UNGE_EXPR)
 		    ? fold (build2 (MAX_EXPR, comp_type, comp_op0, comp_op1))
 		    : fold (build2 (MAX_EXPR, comp_type, comp_op1, comp_op0));
-	      return pedantic_non_lvalue (fold_convert (type, tem));
+	      tem = pedantic_non_lvalue (fold_convert (type, tem));
 	    }
 	  break;
 	case UNEQ_EXPR:
-	  if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg1))))
-	    return pedantic_non_lvalue (fold_convert (type, arg2));
+	  if (! lvalue && !HONOR_NANS (TYPE_MODE (TREE_TYPE (arg1))))
+	    tem = pedantic_non_lvalue (fold_convert (type, arg2));
 	  break;
 	case LTGT_EXPR:
-	  if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg1))))
-	    return pedantic_non_lvalue (fold_convert (type, arg1));
+	  if (! lvalue && !HONOR_NANS (TYPE_MODE (TREE_TYPE (arg1))))
+	    tem = pedantic_non_lvalue (fold_convert (type, arg1));
 	  break;
 	default:
 	  gcc_assert (TREE_CODE_CLASS (comp_code) == tcc_comparison);
@@ -4333,6 +4373,11 @@ fold_cond_expr_with_comparison (tree typ
 	}
     }
 
+  if (tem && (! lvalue || maybe_lvalue_p (tem)))
+    return tem;
+  else
+    tem = NULL;
+
   /* If this is A op C1 ? A : C2 with C1 and C2 constant integers,
      we might still be able to simplify this.  For example,
      if C1 is one less or one more than C2, this might have started
@@ -4347,7 +4392,7 @@ fold_cond_expr_with_comparison (tree typ
       case EQ_EXPR:
 	/* We can replace A with C1 in this case.  */
 	arg1 = fold_convert (type, arg01);
-	return fold (build3 (COND_EXPR, type, arg0, arg1, arg2));
+	tem = fold (build3 (COND_EXPR, type, arg0, arg1, arg2));
 
       case LT_EXPR:
 	/* If C1 is C2 + 1, this is min(A, C2).  */
@@ -4357,8 +4402,8 @@ fold_cond_expr_with_comparison (tree typ
 				const_binop (PLUS_EXPR, arg2,
 					     integer_one_node, 0),
 				OEP_ONLY_CONST))
-	  return pedantic_non_lvalue (fold (build2 (MIN_EXPR,
-						    type, arg1, arg2)));
+	  tem = pedantic_non_lvalue (fold (build2 (MIN_EXPR,
+						   type, arg1, arg2)));
 	break;
 
       case LE_EXPR:
@@ -4369,8 +4414,8 @@ fold_cond_expr_with_comparison (tree typ
 				const_binop (MINUS_EXPR, arg2,
 					     integer_one_node, 0),
 				OEP_ONLY_CONST))
-	  return pedantic_non_lvalue (fold (build2 (MIN_EXPR,
-						    type, arg1, arg2)));
+	  tem = pedantic_non_lvalue (fold (build2 (MIN_EXPR,
+						   type, arg1, arg2)));
 	break;
 
       case GT_EXPR:
@@ -4381,8 +4426,8 @@ fold_cond_expr_with_comparison (tree typ
 				const_binop (MINUS_EXPR, arg2,
 					     integer_one_node, 0),
 				OEP_ONLY_CONST))
-	  return pedantic_non_lvalue (fold (build2 (MAX_EXPR,
-						    type, arg1, arg2)));
+	  tem = pedantic_non_lvalue (fold (build2 (MAX_EXPR,
+						   type, arg1, arg2)));
 	break;
 
       case GE_EXPR:
@@ -4393,8 +4438,8 @@ fold_cond_expr_with_comparison (tree typ
 				const_binop (PLUS_EXPR, arg2,
 					     integer_one_node, 0),
 				OEP_ONLY_CONST))
-	  return pedantic_non_lvalue (fold (build2 (MAX_EXPR,
-						    type, arg1, arg2)));
+	  tem = pedantic_non_lvalue (fold (build2 (MAX_EXPR,
+						   type, arg1, arg2)));
 	break;
       case NE_EXPR:
 	break;
@@ -4402,6 +4447,11 @@ fold_cond_expr_with_comparison (tree typ
 	gcc_unreachable ();
       }
 
+  if (tem && (! lvalue || maybe_lvalue_p (tem)))
+    return tem;
+  else
+    tem = NULL;
+
   return NULL_TREE;
 }
 
Index: gcc/testsuite/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	PR c++/19199
	* g++.dg/expr/lval2.C: New.

Index: gcc/testsuite/g++.dg/expr/lval2.C
===================================================================
RCS file: gcc/testsuite/g++.dg/expr/lval2.C
diff -N gcc/testsuite/g++.dg/expr/lval2.C
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gcc/testsuite/g++.dg/expr/lval2.C 9 Mar 2005 03:59:32 -0000
@@ -0,0 +1,26 @@
+// PR c++/19199
+
+// { dg-do run }
+
+// We used to turn the COND_EXPR lvalue into a MIN_EXPR rvalue, and
+// then return a reference to a temporary in qMin.
+
+#include <assert.h>
+
+enum Foo { A, B };
+
+template<typename T> T &qMin(T &a, T &b) 
+{
+  return a < b ? a : b;
+}
+
+int main (int,  char **)
+{
+  Foo f = A;
+  Foo g = B;
+  Foo &h = qMin(f, g);
+  assert (&h == &f || &h == &g);
+  const Foo &i = qMin((const Foo&)f, (const Foo&)g);
+  assert (&i == &f || &i == &g);
+  return 0;
+}
Index: gcc/testsuite/g++.old-deja/g++.oliva/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	PR c++/19199
	* expr2.C: Fixed.

Index: gcc/testsuite/g++.old-deja/g++.oliva/expr2.C
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.old-deja/g++.oliva/expr2.C,v
retrieving revision 1.5
diff -u -p -r1.5 expr2.C
--- gcc/testsuite/g++.old-deja/g++.oliva/expr2.C 1 May 2003 02:02:47 -0000 1.5
+++ gcc/testsuite/g++.old-deja/g++.oliva/expr2.C 9 Mar 2005 03:59:32 -0000
@@ -1,4 +1,4 @@
-// { dg-do run { xfail *-*-* } }
+// { dg-do run }
 
 // Copyright (C) 2000 Free Software Foundation
 
-- 
Alexandre Oliva             http://www.ic.unicamp.br/~oliva/
Red Hat Compiler Engineer   aoliva@{redhat.com, gcc.gnu.org}
Free Software Evangelist  oliva@{lsd.ic.unicamp.br, gnu.org}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]