diff options
author | Patrick Palka <ppalka@redhat.com> | 2023-12-13 16:46:01 -0500 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2023-12-13 16:46:01 -0500 |
commit | b24c09bfb626271cda345f5a6f0d3a6b6480593d (patch) | |
tree | 5ba81aabbeef93b70b1f524aaed14c90efb26fc2 /gcc | |
parent | ead2b94d602ce758575aa46ec35a51c3157ff9cd (diff) | |
download | gcc-b24c09bfb626271cda345f5a6f0d3a6b6480593d.zip gcc-b24c09bfb626271cda345f5a6f0d3a6b6480593d.tar.gz gcc-b24c09bfb626271cda345f5a6f0d3a6b6480593d.tar.bz2 |
c++: remember candidates that we ignored
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 (maybe accidentally) shares the same name
as its non-template callee.
* g++.dg/overload/error6.C: New test.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/call.cc | 146 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C | 20 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/overload/error6.C | 9 |
3 files changed, 131 insertions, 44 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 1b47d13..aa4111d 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. @@ -6526,6 +6570,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 @@ -6536,20 +6584,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; @@ -6606,22 +6663,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, @@ -6649,13 +6703,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 + stopped at the first bad conversion). Move it to BAD_CANDS to 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 @@ -6669,21 +6724,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; we may want to print all 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 de7570a..50c25cd 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 0000000..86a12ea --- /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" } +} |