Commit 7148dede by Jason Merrill Committed by Jason Merrill

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
parent d321551c
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> 2019-08-14 Jason Merrill <jason@redhat.com>
Implement P0848R3, Conditionally Trivial Special Member Functions. Implement P0848R3, Conditionally Trivial Special Member Functions.
......
...@@ -236,10 +236,23 @@ lvalue_kind (const_tree ref) ...@@ -236,10 +236,23 @@ lvalue_kind (const_tree ref)
gcc_assert (!type_dependent_expression_p (CONST_CAST_TREE (ref))); gcc_assert (!type_dependent_expression_p (CONST_CAST_TREE (ref)));
goto default_; goto default_;
} }
op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 1) {
? TREE_OPERAND (ref, 1) tree op1 = TREE_OPERAND (ref, 1);
: TREE_OPERAND (ref, 0)); if (!op1) op1 = TREE_OPERAND (ref, 0);
op2_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 2)); 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; break;
case MODOP_EXPR: case MODOP_EXPR:
......
...@@ -2308,13 +2308,15 @@ rationalize_conditional_expr (enum tree_code code, tree t, ...@@ -2308,13 +2308,15 @@ rationalize_conditional_expr (enum tree_code code, tree t,
complain); 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 return
build_conditional_expr (loc, TREE_OPERAND (t, 0), build_conditional_expr (loc, TREE_OPERAND (t, 0), op1, op2, complain);
cp_build_unary_op (code, TREE_OPERAND (t, 1), false,
complain),
cp_build_unary_op (code, TREE_OPERAND (t, 2), false,
complain),
complain);
} }
/* Given the TYPE of an anonymous union field inside T, return the /* 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, ...@@ -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)) if (!lvalue_or_else (lhs, lv_assign, complain))
return error_mark_node; return error_mark_node;
tree op1 = cp_build_modify_expr (loc, TREE_OPERAND (lhs, 1), tree op1 = TREE_OPERAND (lhs, 1);
modifycode, rhs, complain); 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 /* When sanitizing undefined behavior, even when rhs doesn't need
stabilization at this point, the sanitization might add extra stabilization at this point, the sanitization might add extra
SAVE_EXPRs in there and so make sure there is no tree sharing 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, ...@@ -8170,8 +8173,9 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
if (sanitize_flags_p (SANITIZE_UNDEFINED if (sanitize_flags_p (SANITIZE_UNDEFINED
| SANITIZE_UNDEFINED_NONDEFAULT)) | SANITIZE_UNDEFINED_NONDEFAULT))
rhs = unshare_expr (rhs); rhs = unshare_expr (rhs);
tree op2 = cp_build_modify_expr (loc, TREE_OPERAND (lhs, 2), tree op2 = TREE_OPERAND (lhs, 2);
modifycode, rhs, complain); if (TREE_CODE (op2) != THROW_EXPR)
op2 = cp_build_modify_expr (loc, op2, modifycode, rhs, complain);
tree cond = build_conditional_expr (input_location, tree cond = build_conditional_expr (input_location,
TREE_OPERAND (lhs, 0), op1, op2, TREE_OPERAND (lhs, 0), op1, op2,
complain); complain);
......
// { dg-do compile { target c++11 } } // { dg-do compile { target c++11 } }
bool b; bool b;
int i;
// { dg-final { scan-assembler "_Z1fIiEDTquL_Z1bEfp_twLi42EET_" } } // { 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_" } } // { 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() int main()
{ {
......
// PR c++/90393
struct S {
S();
S(const S&) {}
};
S f() {
const S m;
return true ? m : throw 0;
}
int main() {}
// 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();
}
...@@ -22,8 +22,8 @@ void fn(int i) ...@@ -22,8 +22,8 @@ void fn(int i)
(i ? throw X() : throw X()); // ok, void (i ? throw X() : throw X()); // ok, void
(i ? i : j) = 1; // ok, int & (i ? i : j) = 1; // ok, int &
(i ? throw X() : j) = 1; // { dg-error "" } non-lvalue (i ? throw X() : j) = 1; // ok, int &
(i ? j : throw X()) = 1; // { dg-error "" } non-lvalue (i ? j : throw X()) = 1; // ok, int &
(i ? throw X() : throw X()) = 1; // { dg-error "" } void (i ? throw X() : throw X()) = 1; // { dg-error "" } void
(i ? (void)1 : i++); // { dg-error "" } ANSI forbids (i ? (void)1 : i++); // { dg-error "" } ANSI forbids
......
...@@ -35,8 +35,8 @@ void fn(int i) ...@@ -35,8 +35,8 @@ void fn(int i)
(i ? throw X() : throw X()); // ok, void (i ? throw X() : throw X()); // ok, void
(i ? i : j) = 1; // ok, int & (i ? i : j) = 1; // ok, int &
(i ? throw X() : j) = 1; // { dg-error "lvalue" } (i ? throw X() : j) = 1; // ok, int &
(i ? j : throw X()) = 1; // { dg-error "lvalue" } (i ? j : throw X()) = 1; // ok, int &
(i ? throw X() : throw X()) = 1; // { dg-error "lvalue" } (i ? throw X() : throw X()) = 1; // { dg-error "lvalue" }
(i ? (void)1 : i++); // { dg-error "throw-expression" } (i ? (void)1 : i++); // { dg-error "throw-expression" }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment