PR c++/90393 - ICE with thow in ?:
authorJason Merrill <jason@redhat.com>
Thu, 15 Aug 2019 21:55:19 +0000 (17:55 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 15 Aug 2019 21:55:19 +0000 (17:55 -0400)
My previous patch for 64372 was incomplete: it only stopped making the
non-throw argument into an rvalue, lvalue_kind still considered the ?:
expression to be an rvalue, leaving us worse than before.

PR c++/64372, DR 1560 - Gratuitous lvalue-to-rvalue conversion in ?:
* tree.c (lvalue_kind): Handle throw in one arm.
* typeck.c (rationalize_conditional_expr): Likewise.
(cp_build_modify_expr): Likewise.

From-SVN: r274550

gcc/cp/ChangeLog
gcc/cp/tree.c
gcc/cp/typeck.c
gcc/testsuite/g++.dg/abi/mangle53.C
gcc/testsuite/g++.dg/expr/cond15.C [new file with mode: 0644]
gcc/testsuite/g++.dg/expr/cond16.C [new file with mode: 0644]
gcc/testsuite/g++.old-deja/g++.eh/cond1.C
gcc/testsuite/g++.old-deja/g++.other/cond5.C

index 25172ace36fcfaca64faac789c1c5bba66b39de4..938ec486d9c15bf2aea5ffa0449c93b9d8786096 100644 (file)
@@ -1,3 +1,12 @@
+2019-08-15  Jason Merrill  <jason@redhat.com>
+
+       PR c++/90393 - ICE with thow in ?:
+
+       PR c++/64372, DR 1560 - Gratuitous lvalue-to-rvalue conversion in ?:
+       * tree.c (lvalue_kind): Handle throw in one arm.
+       * typeck.c (rationalize_conditional_expr): Likewise.
+       (cp_build_modify_expr): Likewise.
+
 2019-08-14  Jason Merrill  <jason@redhat.com>
 
        Implement P0848R3, Conditionally Trivial Special Member Functions.
index bca92100621a49edd28ef8a493192411b8f83b30..17a4df380c1178821133e87a8f023a77b7538341 100644 (file)
@@ -236,10 +236,23 @@ lvalue_kind (const_tree ref)
          gcc_assert (!type_dependent_expression_p (CONST_CAST_TREE (ref)));
          goto default_;
        }
-      op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 1)
-                                   ? TREE_OPERAND (ref, 1)
-                                   : TREE_OPERAND (ref, 0));
-      op2_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 2));
+      {
+       tree op1 = TREE_OPERAND (ref, 1);
+       if (!op1) op1 = TREE_OPERAND (ref, 0);
+       tree op2 = TREE_OPERAND (ref, 2);
+       op1_lvalue_kind = lvalue_kind (op1);
+       op2_lvalue_kind = lvalue_kind (op2);
+       if (!op1_lvalue_kind != !op2_lvalue_kind)
+         {
+           /* The second or the third operand (but not both) is a
+              throw-expression; the result is of the type
+              and value category of the other.  */
+           if (op1_lvalue_kind && TREE_CODE (op2) == THROW_EXPR)
+             op2_lvalue_kind = op1_lvalue_kind;
+           else if (op2_lvalue_kind && TREE_CODE (op1) == THROW_EXPR)
+             op1_lvalue_kind = op2_lvalue_kind;
+         }
+      }
       break;
 
     case MODOP_EXPR:
index 4cc0ee0128d8bf0df8fa809da44880894eac749c..e2a4f285a726191d9b358dfba68e865893126530 100644 (file)
@@ -2308,13 +2308,15 @@ rationalize_conditional_expr (enum tree_code code, tree t,
                                 complain);
     }
 
+  tree op1 = TREE_OPERAND (t, 1);
+  if (TREE_CODE (op1) != THROW_EXPR)
+    op1 = cp_build_unary_op (code, op1, false, complain);
+  tree op2 = TREE_OPERAND (t, 2);
+  if (TREE_CODE (op2) != THROW_EXPR)
+    op2 = cp_build_unary_op (code, op2, false, complain);
+
   return
-    build_conditional_expr (loc, TREE_OPERAND (t, 0),
-                           cp_build_unary_op (code, TREE_OPERAND (t, 1), false,
-                                               complain),
-                           cp_build_unary_op (code, TREE_OPERAND (t, 2), false,
-                                               complain),
-                            complain);
+    build_conditional_expr (loc, TREE_OPERAND (t, 0), op1, op2, complain);
 }
 
 /* Given the TYPE of an anonymous union field inside T, return the
@@ -8160,8 +8162,9 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
        if (!lvalue_or_else (lhs, lv_assign, complain))
          return error_mark_node;
 
-       tree op1 = cp_build_modify_expr (loc, TREE_OPERAND (lhs, 1),
-                                        modifycode, rhs, complain);
+       tree op1 = TREE_OPERAND (lhs, 1);
+       if (TREE_CODE (op1) != THROW_EXPR)
+         op1 = cp_build_modify_expr (loc, op1, modifycode, rhs, complain);
        /* When sanitizing undefined behavior, even when rhs doesn't need
           stabilization at this point, the sanitization might add extra
           SAVE_EXPRs in there and so make sure there is no tree sharing
@@ -8170,8 +8173,9 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
        if (sanitize_flags_p (SANITIZE_UNDEFINED
                              | SANITIZE_UNDEFINED_NONDEFAULT))
          rhs = unshare_expr (rhs);
-       tree op2 = cp_build_modify_expr (loc, TREE_OPERAND (lhs, 2),
-                                        modifycode, rhs, complain);
+       tree op2 = TREE_OPERAND (lhs, 2);
+       if (TREE_CODE (op2) != THROW_EXPR)
+         op2 = cp_build_modify_expr (loc, op2, modifycode, rhs, complain);
        tree cond = build_conditional_expr (input_location,
                                            TREE_OPERAND (lhs, 0), op1, op2,
                                            complain);
index 13f9e711c10e4b4d771c7c78e28e790438e36509..727fd7586f37f8fdf931b23316379cef39df98b6 100644 (file)
@@ -1,10 +1,11 @@
 // { dg-do compile { target c++11 } }
 
 bool b;
+int i;
 // { dg-final { scan-assembler "_Z1fIiEDTquL_Z1bEfp_twLi42EET_" } }
-template <class T> auto f (T t) -> decltype(b?t:throw 42) { return 0; }
+template <class T> auto f (T t) -> decltype(b?t:throw 42) { return i; }
 // { dg-final { scan-assembler "_Z2f2IiEDTquL_Z1bEfp_trET_" } }
-template <class T> auto f2 (T t) -> decltype(b?t:throw) { return 0; }
+template <class T> auto f2 (T t) -> decltype(b?t:throw) { return i; }
 
 int main()
 {
diff --git a/gcc/testsuite/g++.dg/expr/cond15.C b/gcc/testsuite/g++.dg/expr/cond15.C
new file mode 100644 (file)
index 0000000..4a9d057
--- /dev/null
@@ -0,0 +1,13 @@
+// PR c++/90393
+
+struct S {
+  S();
+  S(const S&) {}
+};
+
+S f() {
+  const S m;
+  return true ? m : throw 0;
+}
+
+int main() {}
diff --git a/gcc/testsuite/g++.dg/expr/cond16.C b/gcc/testsuite/g++.dg/expr/cond16.C
new file mode 100644 (file)
index 0000000..796828b
--- /dev/null
@@ -0,0 +1,25 @@
+// PR c++/90393
+// { dg-do run }
+
+int c, d;
+
+struct string {
+  string(const char *p): s(p) { ++c; }
+  ~string() { ++d; }
+  string(const string& str): s(str.s) { ++c; }
+  const char* s;
+  bool empty() const { return !s; }
+};
+    
+string foo()
+{
+  string s("foo");
+  return s.empty() ? throw "empty" : s;
+}
+
+int main()
+{
+  foo();
+  if (c != d)
+    __builtin_abort();
+}
index 1b2de1d10e80d5733675965e2707ae6dd96c698a..fe6d4296ab5036d583723bb1a4ab63cb86fe5ccd 100644 (file)
@@ -22,8 +22,8 @@ void fn(int i)
   (i ? throw X() : throw X());  // ok, void
   
   (i ? i : j) = 1; // ok, int &
-  (i ? throw X() : j) = 1; // { dg-error "" } non-lvalue
-  (i ? j : throw X()) = 1; // { dg-error "" } non-lvalue
+  (i ? throw X() : j) = 1; // ok, int &
+  (i ? j : throw X()) = 1; // ok, int &
   (i ? throw X() : throw X()) = 1;  // { dg-error "" } void
   
   (i ? (void)1 : i++); // { dg-error "" } ANSI forbids
index f4d16e9760bd3f258673e93e94e764c0de4a97a9..0d2baf9edf7f45abfdb8c7ece437643bbcef8c1d 100644 (file)
@@ -35,8 +35,8 @@ void fn(int i)
   (i ? throw X() : throw X());  // ok, void
   
   (i ? i : j) = 1; // ok, int &
-  (i ? throw X() : j) = 1;    // { dg-error "lvalue" }
-  (i ? j : throw X()) = 1;    // { dg-error "lvalue" }
+  (i ? throw X() : j) = 1; // ok, int &
+  (i ? j : throw X()) = 1; // ok, int &
   (i ? throw X() : throw X()) = 1;  // { dg-error "lvalue" }
   
   (i ? (void)1 : i++);        // { dg-error "throw-expression" }