]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: fix -Wparentheses for bool-like class types
authorPatrick Palka <ppalka@redhat.com>
Thu, 21 Dec 2023 20:00:55 +0000 (15:00 -0500)
committerPatrick Palka <ppalka@redhat.com>
Thu, 21 Dec 2023 20:00:55 +0000 (15:00 -0500)
Since r14-4977-g0f2e2080685e75 we now issue a -Wparentheses warning for

  extern std::vector<bool> v;
  bool b = v[0] = true; // warning: suggest parentheses around assignment used as truth value [-Wparentheses]

I intended for that commit to just allow the existing diagnostics to
happen in a template context as well, but the refactoring of
is_assignment_op_expr_p caused us for this -Wparentheses warning from
convert_for_assignment to now consider user-defined operator= expressions
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 here when we didn't use to.

That we now accept user-defined operator= expressions is generally good,
but arguably "boolish" class types should be treated like ordinary bool
as far as the warning is concerned.  To that end this patch suppresses
the warning for such types, specifically when the class type can be
implicitly converted to and assigned from bool.  This criterion captures
the std::vector<bool>::reference of libstdc++ at least.

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
gcc/cp/semantics.cc
gcc/cp/typeck.cc
gcc/testsuite/g++.dg/warn/Wparentheses-34.C [new file with mode: 0644]

index 32ae0e3dbeb6ee2ae999b1101bdb8297a720740e..85adebedeebbb08d7d200637a750b89be78d9570 100644 (file)
@@ -7929,7 +7929,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 */
index 33708365ad50c840ff19aa9c71ae5669e3474c10..e6dba29ee81e97653607982dcc81960063d016ec 100644 (file)
@@ -864,12 +864,70 @@ is_assignment_op_expr_p (tree t)
     && DECL_OVERLOADED_OPERATOR_IS (fndecl, NOP_EXPR);
 }
 
+/* Return true if TYPE is a class type that is convertible to
+   and assignable from bool.  */
+
+static GTY((deletable)) hash_map<tree, bool> *boolish_class_type_p_cache;
+
+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;
+
+  tree ops;
+  bool has_bool_assignment = false;
+  bool has_bool_conversion = 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;
+       }
+    }
+
+  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 is 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 c = 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);
index 1fe30fc7c48bbbbfd134ac4518de23d8d9aeaac4..fa939bc71fd9a7c970533ed7af0d7cf41ac3edf5 100644 (file)
@@ -10387,11 +10387,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 (file)
index 0000000..2100c8a
--- /dev/null
@@ -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])) { }
+}