During overload resolution, we sometimes outright ignore a function from
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
unviable 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 (unviable) 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                                | 149 +++++++++++++-----
 .../g++.dg/diagnostic/param-type-mismatch-2.C |  20 +--
 gcc/testsuite/g++.dg/overload/error6.C        |   9 ++
 3 files changed, 132 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 89d422f7220..3212d5268e0 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,34 @@ 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 unviable "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 +3500,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 +3638,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 +3753,9 @@ splice_viable (struct z_candidate *cands,
   z_candidate *unviable = nullptr;
   z_candidate **unviable_tail = &unviable;
 
+  z_candidate *unviable_ignored = nullptr;
+  z_candidate **unviable_ignored_tail = &unviable_ignored;
+
   /* Be strict inside templates, since build_over_call won't actually
      do the conversions to get pedwarns.  */
   if (processing_template_decl)
@@ -3742,6 +3774,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) ? unviable_ignored_tail
                    : unviable_tail);
       *tail = cand;
       tail = &cand->next;
@@ -3751,7 +3784,8 @@ splice_viable (struct z_candidate *cands,
                   || (!strict_p && non_strictly_viable != nullptr));
 
   /* Combine the lists.  */
-  *unviable_tail = nullptr;
+  *unviable_ignored_tail = nullptr;
+  *unviable_tail = unviable_ignored;
   *non_strictly_viable_tail = unviable;
   *strictly_viable_tail = non_strictly_viable;
 
@@ -3901,6 +3935,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 +4016,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 +5061,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 +6552,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 +6566,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 +6645,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 +6685,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 +6706,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 (cand->template_decl)
+           fn = TI_TEMPLATE (cand->template_decl);
+         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.424.gceadf0f3cf

Reply via email to