On Wed, 20 Dec 2023, Jason Merrill wrote:

> On 12/20/23 17:54, Patrick Palka wrote:
> > On Wed, 20 Dec 2023, Jason Merrill wrote:
> > 
> > > On 12/20/23 17:07, Patrick Palka wrote:
> > > > Bootstrap and regtesting in progress on x86_64-pc-linux-gnu, does this
> > > > look OK for trunk if successful?
> > > > 
> > > > -- >8 --
> > > > 
> > > > Since r14-4977-g0f2e2080685e75 the -Wparentheses warning now undesirably
> > > > warns on the idiom
> > > > 
> > > > Wparentheses-34.C:9:14: warning: suggest parentheses around assignment
> > > > used
> > > > as truth value [-Wparentheses]
> > > >       9 |   b = v[i] = true;
> > > >         |              ^~~~
> > > > 
> > > > where v has type std::vector<bool>.  That commit intended to only extend
> > > > the existing diagnostics so that they happen in a template context as
> > > > well, but the refactoring of is_assignment_op_expr_p caused us for this
> > > > particular -Wparentheses warning (from convert_for_assignment) to now
> > > > consider user-defined operator= instead of just built-in operator=.  And
> > > > since std::vector<bool> is really a bitset, whose operator[] returns a
> > > > class type with such a user-defined operator= (taking bool), we now
> > > > warn.
> > > > 
> > > > But arguably "boolish" class types should be treated like ordinary bool
> > > > as far as the warning is concerned.  To that end this patch relaxes the
> > > > warning for such types, specifically when the (class) type can be
> > > > (implicitly) converted to a and assigned from a bool.  This should cover
> > > > at least implementations of std::vector<bool>::reference.
> > > > 
> > > > gcc/cp/ChangeLog:
> > > > 
> > > >         * cp-tree.h (maybe_warn_unparenthesized_assignment): Add
> > > >         'nested_p' bool parameter.
> > > >         * semantics.cc (is_assignment_op_expr_p): Add 'rhs' bool
> > > >         parameter and set it accordingly.
> > > >         (class_type_is_boolish_cache): Define.
> > > >         (class_type_is_boolish): Define.
> > > >         (maybe_warn_unparenthesized_assignment): Add 'nested_p'
> > > >         bool parameter.  Relax the warning for nested assignments
> > > >         to boolean-like class types.
> > > >         (maybe_convert_cond): Pass nested_p=false to
> > > >         maybe_warn_unparenthesized_assignment.
> > > >         * typeck.cc (convert_for_assignment): Pass nested_p=true to
> > > >         maybe_warn_unparenthesized_assignment.
> > > > 
> > > > gcc/testsuite/ChangeLog:
> > > > 
> > > >         * g++.dg/warn/Wparentheses-34.C: New test.
> > > > ---
> > > >    gcc/cp/cp-tree.h                            |   2 +-
> > > >    gcc/cp/semantics.cc                         | 106
> > > > ++++++++++++++++++--
> > > >    gcc/cp/typeck.cc                            |   2 +-
> > > >    gcc/testsuite/g++.dg/warn/Wparentheses-34.C |  31 ++++++
> > > >    4 files changed, 129 insertions(+), 12 deletions(-)
> > > >    create mode 100644 gcc/testsuite/g++.dg/warn/Wparentheses-34.C
> > > > 
> > > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > > > index 1979572c365..97065cccf3d 100644
> > > > --- a/gcc/cp/cp-tree.h
> > > > +++ b/gcc/cp/cp-tree.h
> > > > @@ -7928,7 +7928,7 @@ extern tree lambda_regenerating_args
> > > > (tree);
> > > >    extern tree most_general_lambda                      (tree);
> > > >    extern tree finish_omp_target                        (location_t, 
> > > > tree,
> > > > tree, bool);
> > > >    extern void finish_omp_target_clauses                (location_t, 
> > > > tree,
> > > > tree *);
> > > > -extern void maybe_warn_unparenthesized_assignment (tree,
> > > > tsubst_flags_t);
> > > > +extern void maybe_warn_unparenthesized_assignment (tree, bool,
> > > > tsubst_flags_t);
> > > >    extern tree cp_check_pragma_unroll           (location_t, tree);
> > > >      /* in tree.cc */
> > > > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > > > index 64839b1ac87..92acd560fa4 100644
> > > > --- a/gcc/cp/semantics.cc
> > > > +++ b/gcc/cp/semantics.cc
> > > > @@ -839,10 +839,11 @@ finish_goto_stmt (tree destination)
> > > >      return add_stmt (build_stmt (input_location, GOTO_EXPR,
> > > > destination));
> > > >    }
> > > >    -/* Returns true if T corresponds to an assignment operator
> > > > expression.
> > > > */
> > > > +/* Returns true if T corresponds to an assignment operator expression,
> > > > +   and sets *LHS to its left-hand-side operand if so.  */
> > > >      static bool
> > > > -is_assignment_op_expr_p (tree t)
> > > > +is_assignment_op_expr_p (tree t, tree *lhs)
> > > >    {
> > > >      if (t == NULL_TREE)
> > > >        return false;
> > > > @@ -850,7 +851,10 @@ is_assignment_op_expr_p (tree t)
> > > >      if (TREE_CODE (t) == MODIFY_EXPR
> > > >          || (TREE_CODE (t) == MODOP_EXPR
> > > >           && TREE_CODE (TREE_OPERAND (t, 1)) == NOP_EXPR))
> > > > -    return true;
> > > > +    {
> > > > +      *lhs = TREE_OPERAND (t, 0);
> > > > +      return true;
> > > > +    }
> > > >        tree call = extract_call_expr (t);
> > > >      if (call == NULL_TREE
> > > > @@ -859,26 +863,107 @@ is_assignment_op_expr_p (tree t)
> > > >        return false;
> > > >        tree fndecl = cp_get_callee_fndecl_nofold (call);
> > > > -  return fndecl != NULL_TREE
> > > > -    && DECL_ASSIGNMENT_OPERATOR_P (fndecl)
> > > > -    && DECL_OVERLOADED_OPERATOR_IS (fndecl, NOP_EXPR);
> > > > +  if (fndecl != NULL_TREE
> > > > +      && DECL_ASSIGNMENT_OPERATOR_P (fndecl)
> > > > +      && DECL_OVERLOADED_OPERATOR_IS (fndecl, NOP_EXPR))
> > > > +    {
> > > > +      *lhs = get_nth_callarg (call, 0);
> > > > +      return true;
> > > > +    }
> > > > +
> > > > +  return false;
> > > >    }
> > > >    +static GTY((deletable)) hash_map<tree, bool>
> > > > *class_type_is_boolish_cache;
> > > > +
> > > > +/* Return true if the class type TYPE can be converted to and assigned
> > > > +   from a boolean.  */
> > > > +
> > > > +static bool
> > > > +class_type_is_boolish (tree type)
> > > > +{
> > > > +  type = TYPE_MAIN_VARIANT (type);
> > > > +  if (!COMPLETE_TYPE_P (type))
> > > > +    return false;
> > > > +
> > > > +  if (bool *r = hash_map_safe_get (class_type_is_boolish_cache, type))
> > > > +    return *r;
> > > > +
> > > > +  bool has_bool_conversion = false;
> > > > +  bool has_bool_assignment = false;
> > > > +
> > > > +  tree ops = lookup_conversions (type);
> > > > +  for (; ops; ops = TREE_CHAIN (ops))
> > > > +    {
> > > > +      tree op = TREE_VALUE (ops);
> > > > +      if (!DECL_NONCONVERTING_P (op)
> > > > +         && TREE_CODE (TREE_TYPE (DECL_NAME (op))) == BOOLEAN_TYPE)
> > > > +       {
> > > > +         has_bool_conversion = true;
> > > > +         break;
> > > > +       }
> > > > +    }
> > > > +
> > > > +  if (!has_bool_conversion)
> > > > +    {
> > > > +      hash_map_safe_put<true> (class_type_is_boolish_cache, type,
> > > > false);
> > > > +      return false;
> > > > +    }
> > > > +
> > > > +  ops = lookup_fnfields (type, assign_op_identifier, /*protect=*/0,
> > > > tf_none);
> > > > +  for (tree op : ovl_range (BASELINK_FUNCTIONS (ops)))
> > > > +    {
> > > > +      op = STRIP_TEMPLATE (op);
> > > > +      if (TREE_CODE (op) != FUNCTION_DECL)
> > > > +       continue;
> > > > +      tree parm = DECL_CHAIN (DECL_ARGUMENTS (op));
> > > > +      tree parm_type = non_reference (TREE_TYPE (parm));
> > > > +      if (TREE_CODE (parm_type) == BOOLEAN_TYPE)
> > > > +       {
> > > > +         has_bool_assignment = true;
> > > > +         break;
> > > > +       }
> > > > +    }
> > > > +
> > > > +  bool boolish = has_bool_conversion && has_bool_assignment;
> > > > +  hash_map_safe_put<true> (class_type_is_boolish_cache, type, boolish);
> > > > +  return boolish;
> > > > +}
> > > > +
> > > > +
> > > >    /* Maybe warn about an unparenthesized 'a = b' (appearing in a
> > > > -   boolean context where 'a == b' might have been intended).  */
> > > > +   boolean context where 'a == b' might have been intended).
> > > > +   NESTED_P is true if T appears as the RHS of another assignment.  */
> > > >      void
> > > > -maybe_warn_unparenthesized_assignment (tree t, tsubst_flags_t complain)
> > > > +maybe_warn_unparenthesized_assignment (tree t, bool nested_p,
> > > > +                                      tsubst_flags_t complain)
> > > >    {
> > > >      t = STRIP_REFERENCE_REF (t);
> > > >    +  tree lhs;
> > > >      if ((complain & tf_warning)
> > > >          && warn_parentheses
> > > > -      && is_assignment_op_expr_p (t)
> > > > +      && is_assignment_op_expr_p (t, &lhs)
> > > >          /* A parenthesized expression would've had this warning
> > > >          suppressed by finish_parenthesized_expr.  */
> > > >          && !warning_suppressed_p (t, OPT_Wparentheses))
> > > >        {
> > > > +      /* In a = b = c, don't warn if b is a boolean-like class type.
> > > > */
> > > > +      if (nested_p)
> > > > +       {
> > > > +         /* The caller already checked this.  */
> > > > +         gcc_checking_assert (TREE_CODE (TREE_TYPE (t)) != 
> > > > BOOLEAN_TYPE);
> > > 
> > > It seems awkward to check BOOLEAN_TYPE in convert_for_assignment and
> > > bool-ish
> > > type here.  Can we check both in one place or the other?
> > 
> > Sounds good, it's easy enough to check both in
> > maybe_warn_unparenthesized_assignment.  Like so?  I've opted to preserve the
> > original slightly stronger form of the check which really checks that
> > (b = c) has bool type rather than b having bool type, to avoid
> > introducing new warnings.
> 
> Do we not want the same for the class case?

IIUC whether we check the type of (b = c) or just that of b only makes a
difference for weird user-defined operator= that returns something other
than *this... and for the kind of bool-like classes we wish to identify,
that won't be the case.

And checking the type of (b = c) certainly is simpler and also more
consistent, so we might as well go with that.  But we should still
perform the check in maybe_warn_unparenthesized_assignment rather
than its caller since boolish_class_type_p is a relatively expensive
predicate so we should check it last.  Like so?  Bootstrap and
regtest running on x86_64-pc-linux-gnu.

-- >8 --

gcc/cp/ChangeLog:

        * cp-tree.h (maybe_warn_unparenthesized_assignment): Add
        'nested_p' bool parameter.
        * semantics.cc
        (boolish_class_type_p_cache): Define.
        (boolish_class_type_p): Define.
        (maybe_warn_unparenthesized_assignment): Add 'nested_p'
        bool parameter.  Suppress the warning for nested assignments
        to bool and bool-like class types.
        (maybe_convert_cond): Pass nested_p=false to
        maybe_warn_unparenthesized_assignment.
        * typeck.cc (convert_for_assignment): Pass nested_p=true to
        maybe_warn_unparenthesized_assignment.  Remove now redundant
        check for 'rhs' having bool type.

gcc/testsuite/ChangeLog:

        * g++.dg/warn/Wparentheses-34.C: New test.
---
 gcc/cp/cp-tree.h                            |  2 +-
 gcc/cp/semantics.cc                         | 71 +++++++++++++++++++--
 gcc/cp/typeck.cc                            |  7 +-
 gcc/testsuite/g++.dg/warn/Wparentheses-34.C | 31 +++++++++
 4 files changed, 101 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/warn/Wparentheses-34.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1979572c365..97065cccf3d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7928,7 +7928,7 @@ extern tree lambda_regenerating_args              (tree);
 extern tree most_general_lambda                        (tree);
 extern tree finish_omp_target                  (location_t, tree, tree, bool);
 extern void finish_omp_target_clauses          (location_t, tree, tree *);
-extern void maybe_warn_unparenthesized_assignment (tree, tsubst_flags_t);
+extern void maybe_warn_unparenthesized_assignment (tree, bool, tsubst_flags_t);
 extern tree cp_check_pragma_unroll             (location_t, tree);
 
 /* in tree.cc */
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 64839b1ac87..46b828d1483 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -864,12 +864,70 @@ is_assignment_op_expr_p (tree t)
     && DECL_OVERLOADED_OPERATOR_IS (fndecl, NOP_EXPR);
 }
 
+static GTY((deletable)) hash_map<tree, bool> *boolish_class_type_p_cache;
+
+/* Return true if the class type TYPE can be converted to and assigned
+   from bool.  */
+
+static bool
+boolish_class_type_p (tree type)
+{
+  type = TYPE_MAIN_VARIANT (type);
+  if (!CLASS_TYPE_P (type) || !COMPLETE_TYPE_P (type))
+    return false;
+
+  if (bool *r = hash_map_safe_get (boolish_class_type_p_cache, type))
+    return *r;
+
+  bool has_bool_assignment = false;
+  bool has_bool_conversion = false;
+
+  tree ops = lookup_fnfields (type, assign_op_identifier,
+                             /*protect=*/0, tf_none);
+  for (tree op : ovl_range (BASELINK_FUNCTIONS (ops)))
+    {
+      op = STRIP_TEMPLATE (op);
+      if (TREE_CODE (op) != FUNCTION_DECL)
+       continue;
+      tree parm = DECL_CHAIN (DECL_ARGUMENTS (op));
+      tree parm_type = non_reference (TREE_TYPE (parm));
+      if (TREE_CODE (parm_type) == BOOLEAN_TYPE)
+       {
+         has_bool_assignment = true;
+         break;
+       }
+    }
+
+  if (has_bool_assignment)
+    {
+      ops = lookup_conversions (type);
+      for (; ops; ops = TREE_CHAIN (ops))
+       {
+         tree op = TREE_VALUE (ops);
+         if (!DECL_NONCONVERTING_P (op)
+             && TREE_CODE (DECL_CONV_FN_TYPE (op)) == BOOLEAN_TYPE)
+           {
+             has_bool_conversion = true;
+             break;
+           }
+       }
+    }
+
+  bool boolish = has_bool_assignment && has_bool_conversion;
+  hash_map_safe_put<true> (boolish_class_type_p_cache, type, boolish);
+  return boolish;
+}
+
+
 /* Maybe warn about an unparenthesized 'a = b' (appearing in a
-   boolean context where 'a == b' might have been intended).  */
+   boolean context where 'a == b' might have been intended).
+   NESTED_P is true if T appears as the RHS of another assignment.  */
 
 void
-maybe_warn_unparenthesized_assignment (tree t, tsubst_flags_t complain)
+maybe_warn_unparenthesized_assignment (tree t, bool nested_p,
+                                      tsubst_flags_t complain)
 {
+  tree type = TREE_TYPE (t);
   t = STRIP_REFERENCE_REF (t);
 
   if ((complain & tf_warning)
@@ -877,7 +935,11 @@ maybe_warn_unparenthesized_assignment (tree t, 
tsubst_flags_t complain)
       && is_assignment_op_expr_p (t)
       /* A parenthesized expression would've had this warning
         suppressed by finish_parenthesized_expr.  */
-      && !warning_suppressed_p (t, OPT_Wparentheses))
+      && !warning_suppressed_p (t, OPT_Wparentheses)
+      /* In ... = a = b, don't warn if a has type bool or bool-like class.  */
+      && (!nested_p
+         || (TREE_CODE (type) != BOOLEAN_TYPE
+             && !boolish_class_type_p (type))))
     {
       warning_at (cp_expr_loc_or_input_loc (t), OPT_Wparentheses,
                  "suggest parentheses around assignment used as truth value");
@@ -903,7 +965,8 @@ maybe_convert_cond (tree cond)
   if (warn_sequence_point && !processing_template_decl)
     verify_sequence_points (cond);
 
-  maybe_warn_unparenthesized_assignment (cond, tf_warning_or_error);
+  maybe_warn_unparenthesized_assignment (cond, /*nested_p=*/false,
+                                        tf_warning_or_error);
 
   /* Do the conversion.  */
   cond = convert_from_reference (cond);
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index a6e2f4ee7da..2f94dcb7938 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -10377,11 +10377,8 @@ convert_for_assignment (tree type, tree rhs,
          }
     }
 
-  /* If -Wparentheses, warn about a = b = c when a has type bool and b
-     does not.  */
-  if (TREE_CODE (type) == BOOLEAN_TYPE
-      && TREE_CODE (TREE_TYPE (rhs)) != BOOLEAN_TYPE)
-    maybe_warn_unparenthesized_assignment (rhs, complain);
+  if (TREE_CODE (type) == BOOLEAN_TYPE)
+    maybe_warn_unparenthesized_assignment (rhs, /*nested_p=*/true, complain);
 
   if (complain & tf_warning)
     warn_for_address_of_packed_member (type, rhs);
diff --git a/gcc/testsuite/g++.dg/warn/Wparentheses-34.C 
b/gcc/testsuite/g++.dg/warn/Wparentheses-34.C
new file mode 100644
index 00000000000..2100c8a193d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wparentheses-34.C
@@ -0,0 +1,31 @@
+// Verify our -Wparentheses warning handles "boolish" class types
+// such as std::vector<bool>'s reference type the same as ordinary
+// bool.
+// { dg-additional-options "-Wparentheses" }
+
+#include <vector>
+
+void f(std::vector<bool> v, int i) {
+  bool b;
+  b = v[i] = true;
+  b = v[i] = v[i+1];
+
+  if (v[i] = 42) { }     // { dg-message "parentheses" }
+  if (v[i] = v[i+1]) { } // { dg-message "parentheses" }
+
+  if ((v[i] = 42)) { }
+  if ((v[i] = v[i+1])) { }
+}
+
+template<class>
+void ft(std::vector<bool> v, int i) {
+  bool b;
+  b = v[i] = true;
+  b = v[i] = v[i+1];
+
+  if (v[i] = 42) { }     // { dg-message "parentheses" }
+  if (v[i] = v[i+1]) { } // { dg-message "parentheses" }
+
+  if ((v[i] = 42)) { }
+  if ((v[i] = v[i+1])) { }
+}
-- 
2.43.0.174.g055bb6e996

Reply via email to