DR 1560 in C++14 fixed this rule to not do a gratuitous lvalue-rvalue conversion in this case.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit c7b8b79f72865740ead84aa602a6bc8651a60a93 Author: Jason Merrill <ja...@redhat.com> Date: Tue May 15 15:46:51 2018 -0400 PR c++/64372 - CWG 1560, gratuitous lvalue-rvalue conversion in ?: * call.c (build_conditional_expr_1): Don't force_rvalue when one arm is a throw-expression. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index f620c0d86e8..09a3618b007 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -4969,56 +4969,33 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, arg3_type = unlowered_expr_type (arg3); if (VOID_TYPE_P (arg2_type) || VOID_TYPE_P (arg3_type)) { - /* Do the conversions. We don't these for `void' type arguments - since it can't have any effect and since decay_conversion - does not handle that case gracefully. */ - if (!VOID_TYPE_P (arg2_type)) - arg2 = decay_conversion (arg2, complain); - if (!VOID_TYPE_P (arg3_type)) - arg3 = decay_conversion (arg3, complain); - arg2_type = TREE_TYPE (arg2); - arg3_type = TREE_TYPE (arg3); - /* [expr.cond] One of the following shall hold: --The second or the third operand (but not both) is a - throw-expression (_except.throw_); the result is of the - type of the other and is an rvalue. + throw-expression (_except.throw_); the result is of the type + and value category of the other. --Both the second and the third operands have type void; the - result is of type void and is an rvalue. - - We must avoid calling force_rvalue for expressions of type - "void" because it will complain that their value is being - used. */ + result is of type void and is a prvalue. */ if (TREE_CODE (arg2) == THROW_EXPR && TREE_CODE (arg3) != THROW_EXPR) { - if (!VOID_TYPE_P (arg3_type)) - { - arg3 = force_rvalue (arg3, complain); - if (arg3 == error_mark_node) - return error_mark_node; - } - arg3_type = TREE_TYPE (arg3); result_type = arg3_type; + is_glvalue = glvalue_p (arg3); } else if (TREE_CODE (arg2) != THROW_EXPR && TREE_CODE (arg3) == THROW_EXPR) { - if (!VOID_TYPE_P (arg2_type)) - { - arg2 = force_rvalue (arg2, complain); - if (arg2 == error_mark_node) - return error_mark_node; - } - arg2_type = TREE_TYPE (arg2); result_type = arg2_type; + is_glvalue = glvalue_p (arg2); } else if (VOID_TYPE_P (arg2_type) && VOID_TYPE_P (arg3_type)) - result_type = void_type_node; + { + result_type = void_type_node; + is_glvalue = false; + } else { if (complain & tf_error) @@ -5037,7 +5014,6 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, return error_mark_node; } - is_glvalue = false; goto valid_operands; } /* [expr.cond] @@ -5155,10 +5131,6 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, && same_type_p (arg2_type, arg3_type)) { result_type = arg2_type; - if (processing_template_decl) - /* Let lvalue_kind know this was a glvalue. */ - result_type = cp_build_reference_type (result_type, xvalue_p (arg2)); - arg2 = mark_lvalue_use (arg2); arg3 = mark_lvalue_use (arg3); goto valid_operands; @@ -5352,6 +5324,13 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, return error_mark_node; valid_operands: + if (processing_template_decl && is_glvalue) + { + /* Let lvalue_kind know this was a glvalue. */ + tree arg = (result_type == arg2_type ? arg2 : arg3); + result_type = cp_build_reference_type (result_type, xvalue_p (arg)); + } + result = build3_loc (loc, COND_EXPR, result_type, arg1, arg2, arg3); /* If the ARG2 and ARG3 are the same and don't have side-effects, diff --git a/gcc/testsuite/g++.dg/cpp1y/dr1560.C b/gcc/testsuite/g++.dg/cpp1y/dr1560.C new file mode 100644 index 00000000000..b21ca98e279 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/dr1560.C @@ -0,0 +1,14 @@ +// Core 1560 +// { dg-do compile { target c++14 } } + +struct A +{ + A(); + A(const A&) = delete; +}; + +void f(bool b) +{ + A a; + b ? a : throw 42; +}