On Feb 27, 2018, Jason Merrill <ja...@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

Reply via email to