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: Tue, 06 Mar 2018 03:13:56 -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> <ory3jdsn4e.fsf@lxoliva.fsfla.org> <CADzB+2=N_xUbDmrQGq0RuCV-4REaLpvNtiR2u_bXuzU=W8gkFQ@mail.gmail.com> <orh8pyx631.fsf@lxoliva.fsfla.org> <CADzB+2mxADG1doFkDMoR44mgzCGdND-nrX9SyiJKDyPGCPMDkg@mail.gmail.com>
On Mar 2, 2018, Jason Merrill <jason@redhat.com> wrote:
> Let's add a comment along the lines of
> /* Let lvalue_kind know this was a glvalue. */
> OK with that change.
Thanks, here's what I'm about to check in.
[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.
---
gcc/cp/call.c | 12 ++++++++----
gcc/cp/parser.c | 4 +++-
gcc/cp/tree.c | 15 +++++++++++++++
gcc/cp/typeck.c | 4 ----
gcc/testsuite/g++.dg/pr84231.C | 29 +++++++++++++++++++++++++++++
5 files changed, 55 insertions(+), 9 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/pr84231.C
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 11fe28292fb1..f83d51f3457e 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 e1acb07d29ef..f21257f41e7b 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 19f1c0629c9a..4cf2126608f0 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 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