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.
For GCC 9 I lean toward reverting the earlier patch rather than applying this
one and thus changing the meaning of well-formed code in the middle of a
release series.
Tested x86_64-pc-linux-gnu, applying to trunk.
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.
---
gcc/cp/tree.c | 21 ++++++++++++----
gcc/cp/typeck.c | 24 +++++++++++--------
gcc/testsuite/g++.dg/abi/mangle53.C | 5 ++--
gcc/testsuite/g++.dg/expr/cond15.C | 13 ++++++++++
gcc/testsuite/g++.dg/expr/cond16.C | 25 ++++++++++++++++++++
gcc/testsuite/g++.old-deja/g++.eh/cond1.C | 4 ++--
gcc/testsuite/g++.old-deja/g++.other/cond5.C | 4 ++--
gcc/cp/ChangeLog | 9 +++++++
8 files changed, 85 insertions(+), 20 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/expr/cond15.C
create mode 100644 gcc/testsuite/g++.dg/expr/cond16.C
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index bca92100621..17a4df380c1 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -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:
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 4cc0ee0128d..e2a4f285a72 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -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);
diff --git a/gcc/testsuite/g++.dg/abi/mangle53.C
b/gcc/testsuite/g++.dg/abi/mangle53.C
index 13f9e711c10..727fd7586f3 100644
--- a/gcc/testsuite/g++.dg/abi/mangle53.C
+++ b/gcc/testsuite/g++.dg/abi/mangle53.C
@@ -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
index 00000000000..4a9d057a757
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/cond15.C
@@ -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
index 00000000000..796828b25c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/cond16.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.old-deja/g++.eh/cond1.C
b/gcc/testsuite/g++.old-deja/g++.eh/cond1.C
index 1b2de1d10e8..fe6d4296ab5 100644
--- a/gcc/testsuite/g++.old-deja/g++.eh/cond1.C
+++ b/gcc/testsuite/g++.old-deja/g++.eh/cond1.C
@@ -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
diff --git a/gcc/testsuite/g++.old-deja/g++.other/cond5.C
b/gcc/testsuite/g++.old-deja/g++.other/cond5.C
index f4d16e9760b..0d2baf9edf7 100644
--- a/gcc/testsuite/g++.old-deja/g++.other/cond5.C
+++ b/gcc/testsuite/g++.old-deja/g++.other/cond5.C
@@ -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" }
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 25172ace36f..938ec486d9c 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,12 @@
+2019-08-15 Jason Merrill <[email protected]>
+
+ 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 <[email protected]>
Implement P0848R3, Conditionally Trivial Special Member Functions.
base-commit: 2d923c00b6664a5388556f14f2a3be8eb4084c1b
--
2.21.0