This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [C++] [PR84231] overload on cond_expr in template
- From: Alexandre Oliva <aoliva at redhat dot com>
- To: Jason Merrill <jason at redhat dot com>
- Cc: gcc-patches List <gcc-patches at gcc dot gnu dot org>
- Date: Wed, 28 Feb 2018 02:24:01 -0300
- Subject: Re: [C++] [PR84231] overload on cond_expr in template
- Authentication-results: sourceware.org; auth=none
- References: <orvaf6gbhl.fsf@lxoliva.fsfla.org> <CADzB+2=8Kbj6gyr+DG4LoRxA+Cmq9OG5QGqPOJpn-18BPfcs3g@mail.gmail.com> <orefl6qpe2.fsf@lxoliva.fsfla.org> <CADzB+2=rxZcJjkK+mw7fN1Ys4Mb4UoO=8robo=R_g=XaL=b56g@mail.gmail.com>
On Feb 27, 2018, Jason Merrill <jason@redhat.com> wrote:
> Perhaps it would be easier to add the REFERENCE_TYPE in
> build_conditional_expr_1, adjusting result_type based on
> processing_template_decl and is_lvalue.
It is, indeed!
Here's the patch, regstrapped on i686- and x86_64-linux-gnu. The only
unexpected glitch was the need for adjusting the fold expr parser to
deal with an indirect_ref, lest g++.dg/cpp1x/fold6.C would fail to
error at the line with the ternary operator.
Ok to install?
[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.
* 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.
---
gcc/cp/call.c | 3 +++
gcc/cp/parser.c | 4 +++-
gcc/cp/tree.c | 8 ++++++++
gcc/cp/typeck.c | 4 ----
gcc/testsuite/g++.dg/pr84231.C | 29 +++++++++++++++++++++++++++++
5 files changed, 43 insertions(+), 5 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/pr84231.C
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 11fe28292fb1..9d98a3d90d25 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5348,6 +5348,9 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
return error_mark_node;
valid_operands:
+ if (processing_template_decl)
+ result_type = cp_build_reference_type (result_type, !is_lvalue);
+
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/cp/parser.c b/gcc/cp/parser.c
index bcee1214c2f3..c483b6ce25ea 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -4961,7 +4961,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 9b9e36a1173f..76148c876b71 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -194,6 +194,14 @@ lvalue_kind (const_tree ref)
break;
case COND_EXPR:
+ /* Except for type-dependent exprs, a REFERENCE_TYPE will
+ indicate whether its result is an lvalue or so.
+ REFERENCE_TYPEs are handled above, so if we reach this point,
+ we know we got an rvalue, unless we have a type-dependent
+ expr. */
+ if (processing_template_decl
+ && !type_dependent_expression_p (CONST_CAST_TREE (ref)))
+ 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 0e7c63dd1973..fba04c49ec2d 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -6565,10 +6565,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/g++.dg/pr84231.C b/gcc/testsuite/g++.dg/pr84231.C
new file mode 100644
index 000000000000..de7c89a2ab69
--- /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<typename T> format& operator%(const T&) { return *this; }
+ template<typename T> format& operator%(T&) { return *this; }
+};
+
+format f;
+
+template <typename>
+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<void>(b);
+}
--
Alexandre Oliva, freedom fighter http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/ FSF Latin America board member
Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer