During overload resolution, we sometimes outright ignore a function in
the overload set and leave no trace of it in the candidates list, for
example when we find a perfect non-template candidate we discard all
function templates, or when the callee is a template-id we discard all
non-template functions.  We should still however make note of these
non-viable functions when diagnosing overload resolution failure, but
that's not possible if they're not present in the returned candidates
list.

To that end, this patch reworks add_candidates to add such ignored
functions to the list.  The new rr_ignored rejection reason is somewhat
of a catch-all; we could perhaps split it up into more specific rejection
reasons, but I leave that as future work.

gcc/cp/ChangeLog:

        * call.cc (enum rejection_reason_code): Add rr_ignored.
        (add_ignored_candidate): Define.
        (ignored_candidate_p): Define.
        (add_template_candidate_real): Do add_ignored_candidate
        instead of returning NULL.
        (splice_viable): Put ignored (non-viable) candidates last.
        (print_z_candidate): Handle ignored candidates.
        (build_new_function_call): Refine shortcut that calls
        cp_build_function_call_vec now that non-templates can
        appear in the candidate list for a template-id call.
        (add_candidates): Replace 'bad_fns' overload with 'bad_cands'
        candidate list.  When not considering a candidate, add it
        to the list as an ignored candidate.  Add all 'bad_cands'
        to the overload set as well.

gcc/testsuite/ChangeLog:

        * g++.dg/diagnostic/param-type-mismatch-2.C: Rename template
        function test_7 that accidentally (perhaps) shares the same
        name as its non-template callee.
        * g++.dg/overload/error6.C: New test.
---
 gcc/cp/call.cc                                | 150 +++++++++++++-----
 .../g++.dg/diagnostic/param-type-mismatch-2.C |  20 +--
 gcc/testsuite/g++.dg/overload/error6.C        |   9 ++
 3 files changed, 133 insertions(+), 46 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/overload/error6.C

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 5d175b93a47..81cc029dddb 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -441,7 +441,8 @@ enum rejection_reason_code {
   rr_template_unification,
   rr_invalid_copy,
   rr_inherited_ctor,
-  rr_constraint_failure
+  rr_constraint_failure,
+  rr_ignored,
 };
 
 struct conversion_info {
@@ -2224,6 +2225,35 @@ add_candidate (struct z_candidate **candidates,
   return cand;
 }
 
+/* FN is a function from the overload set that we outright didn't even
+   consider (for some reason); add it to the list as an non-viable "ignored"
+   candidate.  */
+
+static z_candidate *
+add_ignored_candidate (z_candidate **candidates, tree fn)
+{
+  /* No need to dynamically allocate these.  */
+  static const rejection_reason reason_ignored = { rr_ignored, {} };
+
+  struct z_candidate *cand = (struct z_candidate *)
+    conversion_obstack_alloc (sizeof (struct z_candidate));
+
+  cand->fn = fn;
+  cand->reason = const_cast<rejection_reason *> (&reason_ignored);
+  cand->next = *candidates;
+  *candidates = cand;
+
+  return cand;
+}
+
+/* True iff CAND is a candidate added by add_ignored_candidate.  */
+
+static bool
+ignored_candidate_p (const z_candidate *cand)
+{
+  return cand->reason && cand->reason->code == rr_ignored;
+}
+
 /* Return the number of remaining arguments in the parameter list
    beginning with ARG.  */
 
@@ -3471,7 +3501,7 @@ add_template_candidate_real (struct z_candidate 
**candidates, tree tmpl,
     }
 
   if (len < skip_without_in_chrg)
-    return NULL;
+    return add_ignored_candidate (candidates, tmpl);
 
   if (DECL_CONSTRUCTOR_P (tmpl) && nargs == 2
       && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (first_arg),
@@ -3609,7 +3639,7 @@ add_template_candidate_real (struct z_candidate 
**candidates, tree tmpl,
   if (((flags & (LOOKUP_ONLYCONVERTING|LOOKUP_LIST_INIT_CTOR))
        == LOOKUP_ONLYCONVERTING)
       && DECL_NONCONVERTING_P (fn))
-    return NULL;
+    return add_ignored_candidate (candidates, fn);
 
   if (DECL_CONSTRUCTOR_P (fn) && nargs == 2)
     {
@@ -3724,6 +3754,9 @@ splice_viable (struct z_candidate *cands,
   z_candidate *non_viable = nullptr;
   z_candidate **non_viable_tail = &non_viable;
 
+  z_candidate *non_viable_ignored = nullptr;
+  z_candidate **non_viable_ignored_tail = &non_viable_ignored;
+
   /* Be strict inside templates, since build_over_call won't actually
      do the conversions to get pedwarns.  */
   if (processing_template_decl)
@@ -3742,6 +3775,7 @@ splice_viable (struct z_candidate *cands,
         its viability.  */
       auto& tail = (cand->viable == 1 ? strictly_viable_tail
                    : cand->viable == -1 ? non_strictly_viable_tail
+                   : ignored_candidate_p (cand) ? non_viable_ignored_tail
                    : non_viable_tail);
       *tail = cand;
       tail = &cand->next;
@@ -3751,7 +3785,8 @@ splice_viable (struct z_candidate *cands,
                   || (!strict_p && non_strictly_viable != nullptr));
 
   /* Combine the lists.  */
-  *non_viable_tail = nullptr;
+  *non_viable_ignored_tail = nullptr;
+  *non_viable_tail = non_viable_ignored;
   *non_strictly_viable_tail = non_viable;
   *strictly_viable_tail = non_strictly_viable;
 
@@ -3901,6 +3936,8 @@ print_z_candidate (location_t loc, const char *msgstr,
     inform (cloc, "%s%qT (conversion)", msg, fn);
   else if (candidate->viable == -1)
     inform (cloc, "%s%#qD (near match)", msg, fn);
+  else if (ignored_candidate_p (candidate))
+    inform (cloc, "%s%#qD (ignored)", msg, fn);
   else if (DECL_DELETED_FN (fn))
     inform (cloc, "%s%#qD (deleted)", msg, fn);
   else if (candidate->reversed ())
@@ -3980,6 +4017,8 @@ print_z_candidate (location_t loc, const char *msgstr,
                  "initialization from an expression of the same or derived "
                  "type");
          break;
+       case rr_ignored:
+         break;
        case rr_none:
        default:
          /* This candidate didn't have any issues or we failed to
@@ -5023,7 +5062,12 @@ build_new_function_call (tree fn, vec<tree, va_gc> 
**args,
          // If there is a single (non-viable) function candidate,
          // let the error be diagnosed by cp_build_function_call_vec.
          if (!any_viable_p && candidates && ! candidates->next
-             && (TREE_CODE (candidates->fn) == FUNCTION_DECL))
+             && TREE_CODE (candidates->fn) == FUNCTION_DECL
+             /* A template-id callee consisting of a single (ignored)
+                non-template candidate needs to be diagnosed the
+                ordinary way.  */
+             && (TREE_CODE (fn) != TEMPLATE_ID_EXPR
+                 || candidates->template_decl))
            return cp_build_function_call_vec (candidates->fn, args, complain);
 
          // Otherwise, emit notes for non-viable candidates.
@@ -6509,6 +6553,10 @@ add_candidates (tree fns, tree first_arg, const 
vec<tree, va_gc> *args,
   else /*if (flags & LOOKUP_DEFAULTED)*/
     which = non_templates;
 
+  /* Template candidates that we'll potentially ignore if the
+     perfect candidate optimization succeeds.  */
+  z_candidate *ignored_template_cands = nullptr;
+
   /* During overload resolution, we first consider each function under the
      assumption that we'll eventually find a strictly viable candidate.
      This allows us to circumvent our defacto behavior when checking
@@ -6519,20 +6567,29 @@ add_candidates (tree fns, tree first_arg, const 
vec<tree, va_gc> *args,
      This trick is important for pruning member function overloads according
      to their const/ref-qualifiers (since all 'this' conversions are at
      worst bad) without breaking -fpermissive.  */
-  tree bad_fns = NULL_TREE;
+  z_candidate *bad_cands = nullptr;
   bool shortcut_bad_convs = true;
 
  again:
   for (tree fn : lkp_range (fns))
     {
-      if (check_converting && DECL_NONCONVERTING_P (fn))
-       continue;
-      if (check_list_ctor && !is_list_ctor (fn))
-       continue;
       if (which == templates && TREE_CODE (fn) != TEMPLATE_DECL)
-       continue;
+       {
+         if (template_only)
+           add_ignored_candidate (candidates, fn);
+         continue;
+       }
       if (which == non_templates && TREE_CODE (fn) == TEMPLATE_DECL)
-       continue;
+       {
+         add_ignored_candidate (&ignored_template_cands, fn);
+         continue;
+       }
+      if ((check_converting && DECL_NONCONVERTING_P (fn))
+         || (check_list_ctor && !is_list_ctor (fn)))
+       {
+         add_ignored_candidate (candidates, fn);
+         continue;
+       }
 
       tree fn_first_arg = NULL_TREE;
       const vec<tree, va_gc> *fn_args = args;
@@ -6589,22 +6646,19 @@ add_candidates (tree fns, tree first_arg, const 
vec<tree, va_gc> *args,
        }
 
       if (TREE_CODE (fn) == TEMPLATE_DECL)
-       {
-         if (!add_template_candidate (candidates,
-                                      fn,
-                                      ctype,
-                                      explicit_targs,
-                                      fn_first_arg,
-                                      fn_args,
-                                      return_type,
-                                      access_path,
-                                      conversion_path,
-                                      flags,
-                                      strict,
-                                      shortcut_bad_convs,
-                                      complain))
-           continue;
-       }
+       add_template_candidate (candidates,
+                               fn,
+                               ctype,
+                               explicit_targs,
+                               fn_first_arg,
+                               fn_args,
+                               return_type,
+                               access_path,
+                               conversion_path,
+                               flags,
+                               strict,
+                               shortcut_bad_convs,
+                               complain);
       else
        {
          add_function_candidate (candidates,
@@ -6632,13 +6686,14 @@ add_candidates (tree fns, tree first_arg, const 
vec<tree, va_gc> *args,
        {
          /* This candidate has been tentatively marked non-strictly viable,
             and we didn't compute all argument conversions for it (having
-            stopped at the first bad conversion).  Add the function to BAD_FNS
-            to fully reconsider later if we don't find any strictly viable
-            candidates.  */
+            stopped at the first bad conversion).  Move the candidate to
+            BAD_CANDS to fully reconsider later if we don't find any strictly
+            viable candidates.  */
          if (complain & (tf_error | tf_conv))
            {
-             bad_fns = lookup_add (fn, bad_fns);
-             *candidates = (*candidates)->next;
+             *candidates = cand->next;
+             cand->next = bad_cands;
+             bad_cands = cand;
            }
          else
            /* But if we're in a SFINAE context, just mark this candidate as
@@ -6652,21 +6707,44 @@ add_candidates (tree fns, tree first_arg, const 
vec<tree, va_gc> *args,
   if (which == non_templates && !seen_perfect)
     {
       which = templates;
+      ignored_template_cands = nullptr;
       goto again;
     }
   else if (which == templates
           && !seen_strictly_viable
           && shortcut_bad_convs
-          && bad_fns)
+          && bad_cands)
     {
       /* None of the candidates are strictly viable, so consider again those
-        functions in BAD_FNS, this time without shortcutting bad conversions
+        functions in BAD_CANDS, this time without shortcutting bad conversions
         so that all their argument conversions are computed.  */
       which = either;
-      fns = bad_fns;
+      fns = NULL_TREE;
+      for (z_candidate *cand = bad_cands; cand; cand = cand->next)
+       {
+         tree fn = cand->fn;
+         if (tree ti = cand->template_decl)
+           fn = TI_TEMPLATE (ti);
+         fns = ovl_make (fn, fns);
+       }
       shortcut_bad_convs = false;
+      bad_cands = nullptr;
       goto again;
     }
+
+  if (complain & tf_error)
+    {
+      /* Remember any omitted candidates if we need to print candidates
+        as part of overload resolution failure diagnostics.  */
+      for (z_candidate *omitted_cands : { ignored_template_cands, bad_cands })
+       {
+         z_candidate **omitted_cands_tail = &omitted_cands;
+         while (*omitted_cands_tail)
+           omitted_cands_tail = &(*omitted_cands_tail)->next;
+         *omitted_cands_tail = *candidates;
+         *candidates = omitted_cands;
+       }
+    }
 }
 
 /* Returns 1 if P0145R2 says that the LHS of operator CODE is evaluated first,
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C 
b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
index de7570a6efa..50c25cd49b7 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
@@ -129,22 +129,22 @@ int test_6 (int first, const char *second, float third, 
s6 *ptr)
 /* Template function.  */
 
 template <typename T>
-int test_7 (int one, T two, float three); // { dg-line test_7_decl }
+int callee_7 (int one, T two, float three); // { dg-line callee_7_decl }
 
 int test_7 (int first, const char *second, float third)
 {
-  return test_7 <const char **> (first, second, third); // { dg-line 
test_7_usage }
-  // { dg-message "cannot convert 'const char\\*' to 'const char\\*\\*'" "" { 
target *-*-* } test_7_usage }
+  return callee_7 <const char **> (first, second, third); // { dg-line 
callee_7_usage }
+  // { dg-message "cannot convert 'const char\\*' to 'const char\\*\\*'" "" { 
target *-*-* } callee_7_usage }
   /* { dg-begin-multiline-output "" }
-   return test_7 <const char **> (first, second, third);
-                                         ^~~~~~
-                                         |
-                                         const char*
+   return callee_7 <const char **> (first, second, third);
+                                           ^~~~~~
+                                           |
+                                           const char*
      { dg-end-multiline-output "" } */
-  // { dg-message "initializing argument 2 of 'int test_7\\(int, T, float\\) 
.with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
+  // { dg-message "initializing argument 2 of 'int callee_7\\(int, T, float\\) 
.with T = const char\\*\\*.'" "" { target *-*-* } callee_7_decl }
   /* { dg-begin-multiline-output "" }
- int test_7 (int one, T two, float three);
-                      ~~^~~
+ int callee_7 (int one, T two, float three);
+                        ~~^~~
      { dg-end-multiline-output "" } */
 }
 
diff --git a/gcc/testsuite/g++.dg/overload/error6.C 
b/gcc/testsuite/g++.dg/overload/error6.C
new file mode 100644
index 00000000000..86a12eaa8de
--- /dev/null
+++ b/gcc/testsuite/g++.dg/overload/error6.C
@@ -0,0 +1,9 @@
+// Verify we note even non-template candidates when diagnosing
+// overload resolution failure for a template-id.
+
+template<class T> void f(T); // { dg-message "candidate" }
+void f(int); // { dg-message {candidate: 'void f\(int\)' \(ignored\)} }
+
+int main() {
+  f<int>(0, 0); // { dg-error "no match" }
+}
-- 
2.42.0.482.g2e8e77cbac

Reply via email to