]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: tweaks for explicit conversion fns diagnostic
authorMarek Polacek <polacek@redhat.com>
Fri, 25 Aug 2023 21:55:58 +0000 (17:55 -0400)
committerMarek Polacek <polacek@redhat.com>
Tue, 29 Aug 2023 21:43:08 +0000 (17:43 -0400)
1) When saying that a conversion is erroneous because it would use
an explicit constructor, it might be nice to show where exactly
the explicit constructor is located.  For example, with this patch:

[...]
explicit.C:4:12: note: 'S::S(int)' declared here
    4 |   explicit S(int) { }
      |            ^

2) When a conversion doesn't work out merely because the conversion
function necessary to do the conversion couldn't be used because
it was marked explicit, it would be useful to the user to say so,
rather than just saying "cannot convert".  For example, with this patch:

explicit.C:13:12: error: cannot convert 'S' to 'bool' in initialization
   13 |   bool b = S{1};
      |            ^~~~
      |            |
      |            S
explicit.C:5:12: note: explicit conversion function was not considered
    5 |   explicit operator bool() const { return true; }
      |            ^~~~~~~~

gcc/cp/ChangeLog:

* call.cc (convert_like_internal): Show where the conversion function
was declared.
(maybe_show_nonconverting_candidate): New.
* cp-tree.h (maybe_show_nonconverting_candidate): Declare.
* typeck.cc (convert_for_assignment): Call it.

gcc/testsuite/ChangeLog:

* g++.dg/diagnostic/explicit.C: New test.

gcc/cp/call.cc
gcc/cp/cp-tree.h
gcc/cp/typeck.cc
gcc/testsuite/g++.dg/diagnostic/explicit.C [new file with mode: 0644]

index 23e458d32522c4433bee8d166fb7dd69bc342a97..40d9fdc0516a148049b63af272ded89845f79eaa 100644 (file)
@@ -8459,12 +8459,21 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
                if (pedwarn (loc, 0, "converting to %qT from initializer list "
                             "would use explicit constructor %qD",
                             totype, convfn))
-                 inform (loc, "in C++11 and above a default constructor "
-                         "can be explicit");
+                 {
+                   inform (DECL_SOURCE_LOCATION (convfn), "%qD declared here",
+                           convfn);
+                   inform (loc, "in C++11 and above a default constructor "
+                           "can be explicit");
+                 }
              }
            else
-             error ("converting to %qT from initializer list would use "
-                    "explicit constructor %qD", totype, convfn);
+             {
+               auto_diagnostic_group d;
+               error ("converting to %qT from initializer list would use "
+                      "explicit constructor %qD", totype, convfn);
+               inform (DECL_SOURCE_LOCATION (convfn), "%qD declared here",
+                       convfn);
+             }
          }
 
        /* If we're initializing from {}, it's value-initialization.  */
@@ -14323,4 +14332,28 @@ is_list_ctor (tree decl)
   return true;
 }
 
+/* We know that can_convert_arg_bad already said "no" when trying to convert
+   FROM to TO with ARG and FLAGS.  Try to figure out if it was because
+   an explicit conversion function was skipped when looking for a way to
+   perform the conversion.  At this point we've already printed an error.  */
+
+void
+maybe_show_nonconverting_candidate (tree to, tree from, tree arg, int flags)
+{
+  if (!(flags & LOOKUP_ONLYCONVERTING))
+    return;
+
+  conversion_obstack_sentinel cos;
+  conversion *c = implicit_conversion (to, from, arg, /*c_cast_p=*/false,
+                                      flags & ~LOOKUP_ONLYCONVERTING, tf_none);
+  if (c && !c->bad_p && c->user_conv_p)
+    /* Ay, the conversion would have worked in direct-init context.  */
+    for (; c; c = next_conversion (c))
+      if (c->kind == ck_user
+         && DECL_P (c->cand->fn)
+         && DECL_NONCONVERTING_P (c->cand->fn))
+       inform (DECL_SOURCE_LOCATION (c->cand->fn), "explicit conversion "
+               "function was not considered");
+}
+
 #include "gt-cp-call.h"
index 608d6310e530c34581f0e6f8b732e6cc229c89db..6b225ca182f3ed0b3359cf1dbc97f454669e442a 100644 (file)
@@ -6727,6 +6727,7 @@ extern bool cp_handle_deprecated_or_unavailable (tree, tsubst_flags_t = tf_warni
 extern void cp_warn_deprecated_use_scopes      (tree);
 extern tree get_function_version_dispatcher    (tree);
 extern bool any_template_arguments_need_structural_equality_p (tree);
+extern void maybe_show_nonconverting_candidate (tree, tree, tree, int);
 
 /* in class.cc */
 extern tree build_vfield_ref                   (tree, tree);
index d5c0c85ed51b93b6e2a27ca2807e890047e57083..459739d5866b7361cccbdba59e031e67ceecd136 100644 (file)
@@ -10262,6 +10262,8 @@ convert_for_assignment (tree type, tree rhs,
                {
                  range_label_for_type_mismatch label (rhstype, type);
                  gcc_rich_location richloc (rhs_loc, has_loc ? &label : NULL);
+                 auto_diagnostic_group d;
+
                  switch (errtype)
                    {
                    case ICR_DEFAULT_ARGUMENT:
@@ -10296,6 +10298,10 @@ convert_for_assignment (tree type, tree rhs,
                      gcc_unreachable();
                  }
                }
+
+             /* See if we can be more helpful.  */
+             maybe_show_nonconverting_candidate (type, rhstype, rhs, flags);
+
              if (TYPE_PTR_P (rhstype)
                  && TYPE_PTR_P (type)
                  && CLASS_TYPE_P (TREE_TYPE (rhstype))
diff --git a/gcc/testsuite/g++.dg/diagnostic/explicit.C b/gcc/testsuite/g++.dg/diagnostic/explicit.C
new file mode 100644 (file)
index 0000000..122accb
--- /dev/null
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++11 } }
+
+struct A {
+  long int l;
+};
+
+struct S {
+  explicit S(int) { }
+  explicit operator bool() const { return true; } // { dg-message "explicit conversion function was not considered" }
+  explicit operator int() const { return 42; } // { dg-message "explicit conversion function was not considered" }
+  explicit operator char() const { return 42; } // { dg-message "explicit conversion function was not considered" }
+  explicit operator double() const { return 42.; } // { dg-message "explicit conversion function was not considered" }
+  explicit operator float() const { return 42.; } // { dg-message "explicit conversion function was not considered" }
+  explicit operator long() const { return 42.; } // { dg-message "explicit conversion function was not considered" }
+};
+
+double
+f (char)
+{
+  return S{2}; // { dg-error "cannot convert .S. to .double. in return" }
+}
+
+void
+g ()
+{
+  S s = {1}; // { dg-error "would use explicit constructor" }
+  bool b = S{1}; // { dg-error "cannot convert .S. to .bool. in initialization" }
+  int i;
+  i = S{2}; // { dg-error "cannot convert .S. to .int. in assignment" }
+  f (S{3}); // { dg-error "cannot convert .S. to .char." }
+  A a{ S{4} }; // { dg-error "cannot convert .S. to .long int. in initialization" }
+  float arr[1] = { S{5} }; // { dg-error "cannot convert .S. to .float. in initialization" }
+}