From 23d63b459c032c41f99a7c735a33558e77d6baf7 Mon Sep 17 00:00:00 2001 From: Alexandre Oliva Date: Tue, 6 Mar 2018 06:25:12 +0000 Subject: [PATCH] [C++] [PR84231] overload on cond_expr in template A non-type-dependent COND_EXPR within a template is reconstructed with the original operands, after one with non-dependent proxies is built to determine its result type. This is problematic because the operands of a COND_EXPR determined to be an rvalue may have been converted to denote their rvalue nature. The reconstructed one, however, won't have such conversions, so lvalue_kind may not recognize it as an rvalue, which may lead to e.g. incorrect overload resolution decisions. If we mistake such a COND_EXPR for an lvalue, overload resolution might regard a conversion sequence that binds it to a non-const reference as viable, and then select that over one that binds it to a const reference. Only after template substitution would we rebuild the COND_EXPR, realize it is an rvalue, and conclude the reference binding is ill-formed, but at that point we'd have long discarded any alternate candidates we could have used. This patch modifies the logic that determines whether a (non-type-dependent) COND_EXPR in a template is an lvalue, to rely on its type, more specifically, on the presence of a REFERENCE_TYPE wrapper. In order to avoid a type bootstrapping problem, the REFERENCE_TYPE that wraps the type of some such COND_EXPRs is introduced earlier, so that we don't have to test for lvalueness of the expression using the very code that we wish to change. for gcc/cp/ChangeLog PR c++/84231 * tree.c (lvalue_kind): Use presence/absence of REFERENCE_TYPE only while processing template decls. * typeck.c (build_x_conditional_expr): Move wrapping of reference type around type... * call.c (build_conditional_expr_1): ... here. Rename is_lvalue to is_glvalue. * parser.c (cp_parser_fold_expression): Catch REFERENCE_REF_P INDIRECT_REF of COND_EXPR too. for gcc/testsuite/ChangeLog PR c++/84231 * g++.dg/pr84231.C: New. From-SVN: r258271 --- gcc/cp/ChangeLog | 10 ++++++++++ gcc/cp/call.c | 12 ++++++++---- gcc/cp/parser.c | 4 +++- gcc/cp/tree.c | 15 +++++++++++++++ gcc/cp/typeck.c | 4 ---- gcc/testsuite/ChangeLog | 3 +++ gcc/testsuite/g++.dg/pr84231.C | 29 +++++++++++++++++++++++++++++ 7 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/g++.dg/pr84231.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 3623405ded0..bd2e10431a9 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,15 @@ 2018-03-06 Alexandre Oliva + PR c++/84231 + * tree.c (lvalue_kind): Use presence/absence of REFERENCE_TYPE + only while processing template decls. + * typeck.c (build_x_conditional_expr): Move wrapping of + reference type around type... + * call.c (build_conditional_expr_1): ... here. Rename + is_lvalue to is_glvalue. + * parser.c (cp_parser_fold_expression): Catch REFERENCE_REF_P + INDIRECT_REF of COND_EXPR too. + PR c++/84593 * init.c (build_zero_init_1): Zero-initialize references. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 11fe28292fb..f83d51f3457 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -4782,7 +4782,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, tree arg3_type; tree result = NULL_TREE; tree result_type = NULL_TREE; - bool is_lvalue = true; + bool is_glvalue = true; struct z_candidate *candidates = 0; struct z_candidate *cand; void *p; @@ -5037,7 +5037,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, return error_mark_node; } - is_lvalue = false; + is_glvalue = false; goto valid_operands; } /* [expr.cond] @@ -5155,6 +5155,10 @@ 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; @@ -5167,7 +5171,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands (_over.match.oper_, _over.built_). */ - is_lvalue = false; + is_glvalue = false; if (!same_type_p (arg2_type, arg3_type) && (CLASS_TYPE_P (arg2_type) || CLASS_TYPE_P (arg3_type))) { @@ -5361,7 +5365,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3, /* We can't use result_type below, as fold might have returned a throw_expr. */ - if (!is_lvalue) + if (!is_glvalue) { /* Expand both sides into the same slot, hopefully the target of the ?: expression. We used to check for TARGET_EXPRs here, diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 460b5eaf10e..a19bbe1e1d0 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -4963,7 +4963,9 @@ cp_parser_fold_expression (cp_parser *parser, tree expr1) else if (is_binary_op (TREE_CODE (expr1))) error_at (location_of (expr1), "binary expression in operand of fold-expression"); - else if (TREE_CODE (expr1) == COND_EXPR) + else if (TREE_CODE (expr1) == COND_EXPR + || (REFERENCE_REF_P (expr1) + && TREE_CODE (TREE_OPERAND (expr1, 0)) == COND_EXPR)) error_at (location_of (expr1), "conditional expression in operand of fold-expression"); diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 19f1c0629c9..4cf2126608f 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -194,6 +194,21 @@ lvalue_kind (const_tree ref) break; case COND_EXPR: + if (processing_template_decl) + { + /* Within templates, a REFERENCE_TYPE will indicate whether + the COND_EXPR result is an ordinary lvalue or rvalueref. + Since REFERENCE_TYPEs are handled above, if we reach this + point, we know we got a plain rvalue. Unless we have a + type-dependent expr, that is, but we shouldn't be testing + lvalueness if we can't even tell the types yet! */ + gcc_assert (!type_dependent_expression_p (CONST_CAST_TREE (ref))); + if (CLASS_TYPE_P (TREE_TYPE (ref)) + || TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE) + return clk_class; + else + return clk_none; + } op1_lvalue_kind = lvalue_kind (TREE_OPERAND (ref, 1) ? TREE_OPERAND (ref, 1) : TREE_OPERAND (ref, 0)); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index f535902231d..0c2ebd1b3e4 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -6567,10 +6567,6 @@ build_x_conditional_expr (location_t loc, tree ifexp, tree op1, tree op2, { tree min = build_min_non_dep (COND_EXPR, expr, orig_ifexp, orig_op1, orig_op2); - /* Remember that the result is an lvalue or xvalue. */ - if (glvalue_p (expr) && !glvalue_p (min)) - TREE_TYPE (min) = cp_build_reference_type (TREE_TYPE (min), - !lvalue_p (expr)); expr = convert_from_reference (min); } return expr; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 9c94d366646..3de87e95b56 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,8 @@ 2018-03-06 Alexandre Oliva + PR c++/84231 + * g++.dg/pr84231.C: New. + PR c++/84593 * g++.dg/cpp1y/pr84593.C: New. diff --git a/gcc/testsuite/g++.dg/pr84231.C b/gcc/testsuite/g++.dg/pr84231.C new file mode 100644 index 00000000000..de7c89a2ab6 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr84231.C @@ -0,0 +1,29 @@ +// PR c++/84231 - overload resolution with cond_expr in a template + +// { dg-do compile } + +struct format { + template format& operator%(const T&) { return *this; } + template format& operator%(T&) { return *this; } +}; + +format f; + +template +void function_template(bool b) +{ + // Compiles OK with array lvalue: + f % (b ? "x" : "x"); + + // Used to fails with pointer rvalue: + f % (b ? "" : "x"); +} + +void normal_function(bool b) +{ + // Both cases compile OK in non-template function: + f % (b ? "x" : "x"); + f % (b ? "" : "x"); + + function_template(b); +} -- 2.30.2