[C++] [PR84231] overload on cond_expr in template
authorAlexandre Oliva <aoliva@redhat.com>
Tue, 6 Mar 2018 06:25:12 +0000 (06:25 +0000)
committerAlexandre Oliva <aoliva@gcc.gnu.org>
Tue, 6 Mar 2018 06:25:12 +0000 (06:25 +0000)
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
gcc/cp/call.c
gcc/cp/parser.c
gcc/cp/tree.c
gcc/cp/typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/pr84231.C [new file with mode: 0644]

index 3623405..bd2e104 100644 (file)
@@ -1,5 +1,15 @@
 2018-03-06  Alexandre Oliva <aoliva@redhat.com>
 
+       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.
 
index 11fe282..f83d51f 100644 (file)
@@ -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,
index 460b5ea..a19bbe1 100644 (file)
@@ -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");
 
index 19f1c06..4cf2126 100644 (file)
@@ -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));
index f535902..0c2ebd1 100644 (file)
@@ -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;
index 9c94d36..3de87e9 100644 (file)
@@ -1,5 +1,8 @@
 2018-03-06  Alexandre Oliva <aoliva@redhat.com>
 
+       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 (file)
index 0000000..de7c89a
--- /dev/null
@@ -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);
+}