From: Jason Merrill Date: Thu, 15 Aug 2019 21:55:19 +0000 (-0400) Subject: PR c++/90393 - ICE with thow in ?: X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=7148dede8a84e17cc0b00190d76fabbc1a717654;p=gcc.git PR c++/90393 - ICE with thow in ?: 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 --- 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 + + 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 Implement P0848R3, Conditionally Trivial Special Member Functions. 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 auto f (T t) -> decltype(b?t:throw 42) { return 0; } +template auto f (T t) -> decltype(b?t:throw 42) { return i; } // { dg-final { scan-assembler "_Z2f2IiEDTquL_Z1bEfp_trET_" } } -template auto f2 (T t) -> decltype(b?t:throw) { return 0; } +template 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" }