https://gcc.gnu.org/g:339246fb9ef4cac84126138093303f309071e40e
commit r15-5995-g339246fb9ef4cac84126138093303f309071e40e Author: David Malcolm <dmalc...@redhat.com> Date: Fri Dec 6 13:40:55 2024 -0500 c++: use diagnostic nesting [PR116253] This patch uses the nested diagnostics capabilities added in the earlier patch in the C++ frontend. With this, and enabling the non-standard text formatting via: -fdiagnostics-set-output=text:experimental-nesting=yes and using: -std=c++20 -fconcepts-diagnostics-depth=2 then the output for the example in SG15's P3358R0 ("SARIF for Structured Diagnostics") is: P3358R0.C: In function ‘int main()’: P3358R0.C:26:6: error: no matching function for call to ‘pet(lizard)’ 26 | pet(lizard{}); | ~~~^~~~~~~~~~ • note: candidate: ‘template<class auto:1> requires pettable<auto:1> void pet(auto:1)’ P3358R0.C:21:6: 21 | void pet(pettable auto t); | ^~~ • note: template argument deduction/substitution failed: • note: constraints not satisfied • P3358R0.C: In substitution of ‘template<class auto:1> requires pettable<auto:1> void pet(auto:1) [with auto:1 = lizard]’: • required from here P3358R0.C:26:6: 26 | pet(lizard{}); | ~~~^~~~~~~~~~ • required for the satisfaction of ‘pettable<auto:1>’ [with auto:1 = lizard] P3358R0.C:19:9: 19 | concept pettable = has_member_pet<T> or has_default_pet<T>; | ^~~~~~~~ • note: no operand of the disjunction is satisfied P3358R0.C:19:38: 19 | concept pettable = has_member_pet<T> or has_default_pet<T>; | ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~ • note: the operand ‘has_member_pet<T>’ is unsatisfied because P3358R0.C:19:20: 19 | concept pettable = has_member_pet<T> or has_default_pet<T>; | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ • required for the satisfaction of ‘has_member_pet<T>’ [with T = lizard] P3358R0.C:13:9: 13 | concept has_member_pet = requires(T t) { t.pet(); }; | ^~~~~~~~~~~~~~ • required for the satisfaction of ‘pettable<auto:1>’ [with auto:1 = lizard] P3358R0.C:19:9: 19 | concept pettable = has_member_pet<T> or has_default_pet<T>; | ^~~~~~~~ • in requirements with ‘T t’ [with T = lizard] P3358R0.C:13:26: 13 | concept has_member_pet = requires(T t) { t.pet(); }; | ^~~~~~~~~~~~~~~~~~~~~~~~~~ • note: the required expression ‘t.pet()’ is invalid, because P3358R0.C:13:47: 13 | concept has_member_pet = requires(T t) { t.pet(); }; | ~~~~~^~ • error: ‘struct lizard’ has no member named ‘pet’ P3358R0.C:13:44: 13 | concept has_member_pet = requires(T t) { t.pet(); }; | ~~^~~ • note: the operand ‘has_default_pet<T>’ is unsatisfied because P3358R0.C:19:41: 19 | concept pettable = has_member_pet<T> or has_default_pet<T>; | ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ • required for the satisfaction of ‘has_default_pet<T>’ [with T = lizard] P3358R0.C:16:9: 16 | concept has_default_pet = T::is_pettable; | ^~~~~~~~~~~~~~~ • required for the satisfaction of ‘pettable<auto:1>’ [with auto:1 = lizard] P3358R0.C:19:9: 19 | concept pettable = has_member_pet<T> or has_default_pet<T>; | ^~~~~~~~ • error: ‘is_pettable’ is not a member of ‘lizard’ P3358R0.C:16:30: 16 | concept has_default_pet = T::is_pettable; | ^~~~~~~~~~~ • note: candidate: ‘void pet(dog)’ P3358R0.C:9:6: 9 | void pet(dog); | ^~~ • note: no known conversion for argument 1 from ‘lizard’ to ‘dog’ P3358R0.C:9:10: 9 | void pet(dog); | ^~~ • note: candidate: ‘void pet(cat)’ P3358R0.C:10:6: 10 | void pet(cat); | ^~~ • note: no known conversion for argument 1 from ‘lizard’ to ‘cat’ P3358R0.C:10:10: 10 | void pet(cat); | ^~~ showing the hierarchical structure of the messages; ideally there would be a UI here allowing the user to expand/collapse the messages to drill out into the detail they are interested in. The structure is also captured in SARIF output (via the "nestingLevel" property). gcc/cp/ChangeLog: PR other/116253 * call.cc (print_conversion_rejection): Remove leading space from diagnostic messages. (print_conversion_rejection): Likewise. (print_arity_information): Likewise. (print_z_candidate): Likewise. Add auto_diagnostic_nesting_level before calls to fn_type_unification and diagnose_constraints. (print_z_candidates): Add auto_diagnostic_nesting_level before looping over candidates. (conversion_null_warnings): Remove leading space from diagnostic messages. (maybe_inform_about_fndecl_for_bogus_argument_init): Likewise. * constraint.cc (tsubst_valid_expression_requirement): Add auto_diagnostic_nesting_level when showing why the expression is invalid. (satisfy_disjunction): Likewise when showing operans, and again when replaying each branch of the disjunction. (diagnose_constraints): Likewise when replaying satisfaction. * error.cc (cp_diagnostic_text_starter): Set prefix. (print_instantiation_full_context): Only show the file if we're not showing nesting or the user has opted in to showing location information in nested diagnostics. (class auto_context_line): New. (print_instantiation_partial_context_line): Replace calls to print_location and to diagnostic_show_locus with an auto_context_line. (print_instantiation_partial_context): Replace calls to print_location with an auto_context_line. (maybe_print_constexpr_context): Likewise. (print_constrained_decl_info): Likewise. (print_concept_check_info): Likewise. (print_constraint_context_head): Likewise. (print_requires_expression_info): Likewise. gcc/testsuite/ChangeLog: PR other/116253 * g++.dg/concepts/nested-diagnostics-1-truncated.C: New test. * g++.dg/concepts/nested-diagnostics-1.C: New test. * g++.dg/concepts/nested-diagnostics-2.C: New test. Signed-off-by: David Malcolm <dmalc...@redhat.com> Diff: --- gcc/cp/call.cc | 69 ++++++++------- gcc/cp/constraint.cc | 5 ++ gcc/cp/error.cc | 99 ++++++++++++++++++---- .../concepts/nested-diagnostics-1-truncated.C | 41 +++++++++ .../g++.dg/concepts/nested-diagnostics-1.C | 51 +++++++++++ .../g++.dg/concepts/nested-diagnostics-2.C | 37 ++++++++ 6 files changed, 257 insertions(+), 45 deletions(-) diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 9fbc1602b0de..c4dc89f3205e 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -3886,18 +3886,18 @@ print_conversion_rejection (location_t loc, struct conversion_info *info, /* Conversion of implicit `this' argument failed. */ if (!TYPE_P (info->from)) /* A bad conversion for 'this' must be discarding cv-quals. */ - inform (loc, " passing %qT as %<this%> " + inform (loc, "passing %qT as %<this%> " "argument discards qualifiers", from); else - inform (loc, " no known conversion for implicit " + inform (loc, "no known conversion for implicit " "%<this%> parameter from %qH to %qI", from, info->to_type); } else if (!TYPE_P (info->from)) { if (info->n_arg >= 0) - inform (loc, " conversion of argument %d would be ill-formed:", + inform (loc, "conversion of argument %d would be ill-formed:", info->n_arg + 1); iloc_sentinel ils = loc; perform_implicit_conversion (info->to_type, info->from, @@ -3905,13 +3905,13 @@ print_conversion_rejection (location_t loc, struct conversion_info *info, } else if (info->n_arg == -2) /* Conversion of conversion function return value failed. */ - inform (loc, " no known conversion from %qH to %qI", + inform (loc, "no known conversion from %qH to %qI", from, info->to_type); else { if (TREE_CODE (fn) == FUNCTION_DECL) loc = get_fndecl_argument_location (fn, info->n_arg); - inform (loc, " no known conversion for argument %d from %qH to %qI", + inform (loc, "no known conversion for argument %d from %qH to %qI", info->n_arg + 1, from, info->to_type); } } @@ -3925,13 +3925,13 @@ print_arity_information (location_t loc, unsigned int have, unsigned int want, { if (least_p) inform_n (loc, want, - " candidate expects at least %d argument, %d provided", - " candidate expects at least %d arguments, %d provided", + "candidate expects at least %d argument, %d provided", + "candidate expects at least %d arguments, %d provided", want, have); else inform_n (loc, want, - " candidate expects %d argument, %d provided", - " candidate expects %d arguments, %d provided", + "candidate expects %d argument, %d provided", + "candidate expects %d arguments, %d provided", want, have); } @@ -3987,11 +3987,12 @@ print_z_candidate (location_t loc, const char *msgstr, if (fn != candidate->fn) { cloc = location_of (candidate->fn); - inform (cloc, " inherited here"); + inform (cloc, "inherited here"); } /* Give the user some information about why this candidate failed. */ if (candidate->reason != NULL) { + auto_diagnostic_nesting_level sentinel; struct rejection_reason *r = candidate->reason; switch (r->code) @@ -4008,13 +4009,13 @@ print_z_candidate (location_t loc, const char *msgstr, print_conversion_rejection (cloc, &r->u.bad_conversion, fn); break; case rr_explicit_conversion: - inform (cloc, " return type %qT of explicit conversion function " + inform (cloc, "return type %qT of explicit conversion function " "cannot be converted to %qT with a qualification " "conversion", r->u.conversion.from, r->u.conversion.to_type); break; case rr_template_conversion: - inform (cloc, " conversion from return type %qT of template " + inform (cloc, "conversion from return type %qT of template " "conversion function specialization to %qT is not an " "exact match", r->u.conversion.from, r->u.conversion.to_type); @@ -4025,33 +4026,39 @@ print_z_candidate (location_t loc, const char *msgstr, them here. */ if (r->u.template_unification.tmpl == NULL_TREE) { - inform (cloc, " substitution of deduced template arguments " + inform (cloc, "substitution of deduced template arguments " "resulted in errors seen above"); break; } /* Re-run template unification with diagnostics. */ - inform (cloc, " template argument deduction/substitution failed:"); - fn_type_unification (r->u.template_unification.tmpl, - r->u.template_unification.explicit_targs, - (make_tree_vec - (r->u.template_unification.num_targs)), - r->u.template_unification.args, - r->u.template_unification.nargs, - r->u.template_unification.return_type, - r->u.template_unification.strict, - r->u.template_unification.flags, - NULL, true, false); + inform (cloc, "template argument deduction/substitution failed:"); + { + auto_diagnostic_nesting_level sentinel; + fn_type_unification (r->u.template_unification.tmpl, + r->u.template_unification.explicit_targs, + (make_tree_vec + (r->u.template_unification.num_targs)), + r->u.template_unification.args, + r->u.template_unification.nargs, + r->u.template_unification.return_type, + r->u.template_unification.strict, + r->u.template_unification.flags, + NULL, true, false); + } break; case rr_invalid_copy: inform (cloc, - " a constructor taking a single argument of its own " + "a constructor taking a single argument of its own " "class type is invalid"); break; case rr_constraint_failure: - diagnose_constraints (cloc, fn, NULL_TREE); + { + auto_diagnostic_nesting_level sentinel; + diagnose_constraints (cloc, fn, NULL_TREE); + } break; case rr_inherited_ctor: - inform (cloc, " an inherited constructor is not a candidate for " + inform (cloc, "an inherited constructor is not a candidate for " "initialization from an expression of the same or derived " "type"); break; @@ -4124,6 +4131,8 @@ print_z_candidates (location_t loc, struct z_candidate *candidates, if (only_viable_p.is_unknown ()) only_viable_p = candidates->viable == 1; + auto_diagnostic_nesting_level sentinel; + for (; candidates; candidates = candidates->next) { if (only_viable_p.is_true () && candidates->viable != 1) @@ -8336,7 +8345,7 @@ conversion_null_warnings (tree totype, tree expr, tree fn, int argnum) "passing NULL to non-pointer argument %P of %qD", argnum, fn)) inform (get_fndecl_argument_location (fn, argnum), - " declared here"); + "declared here"); } else warning_at (loc, OPT_Wconversion_null, @@ -8355,7 +8364,7 @@ conversion_null_warnings (tree totype, tree expr, tree fn, int argnum) "converting %<false%> to pointer type for argument " "%P of %qD", argnum, fn)) inform (get_fndecl_argument_location (fn, argnum), - " declared here"); + "declared here"); } else warning_at (loc, OPT_Wconversion_null, @@ -8430,7 +8439,7 @@ maybe_inform_about_fndecl_for_bogus_argument_init (tree fn, int argnum, gcc_rich_location richloc (get_fndecl_argument_location (fn, argnum)); richloc.set_highlight_color (highlight_color); inform (&richloc, - " initializing argument %P of %qD", argnum, fn); + "initializing argument %P of %qD", argnum, fn); } } diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 3389775036c1..2af1bbc016b9 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -1324,6 +1324,7 @@ tsubst_valid_expression_requirement (tree t, tree args, sat_info info) if (diagnosing_failed_constraint::replay_errors_p ()) { inform (loc, "the required expression %qE is invalid, because", t); + auto_diagnostic_nesting_level sentinel; if (r == error_mark_node) tsubst_expr (t, args, info.complain, info.in_decl); else @@ -2232,6 +2233,7 @@ satisfy_disjunction (tree t, tree args, sat_info info) "no operand of the disjunction is satisfied"); if (diagnosing_failed_constraint::replay_errors_p ()) { + auto_diagnostic_nesting_level sentinel; /* Replay the error in each branch of the disjunction. */ auto_vec<tree_pair> operands; collect_operands_of_disjunction (t, &operands); @@ -2243,6 +2245,7 @@ satisfy_disjunction (tree t, tree args, sat_info info) disj_expr.get_start (), disj_expr.get_finish ()); inform (loc, "the operand %qE is unsatisfied because", op); + auto_diagnostic_nesting_level sentinel; satisfy_constraint_r (norm_op, args, info); } } @@ -3293,6 +3296,8 @@ diagnose_constraints (location_t loc, tree t, tree args) if (concepts_diagnostics_max_depth == 0) return; + auto_diagnostic_nesting_level sentinel; + /* Replay satisfaction, but diagnose unsatisfaction. */ sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true); constraint_satisfaction_value (t, args, noisy); diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index b3ab8fa8a7f7..3b22a8f40723 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -3647,6 +3647,8 @@ static void cp_diagnostic_text_starter (diagnostic_text_output_format &text_output, const diagnostic_info *diagnostic) { + pp_set_prefix (text_output.get_printer (), + text_output.build_indent_prefix (true)); text_output.report_current_module (diagnostic_location (diagnostic)); cp_print_error_function (text_output, diagnostic); maybe_print_instantiation_context (text_output); @@ -3814,13 +3816,19 @@ print_instantiation_full_context (diagnostic_text_output_format &text_output) if (p) { + bool show_file + = ((!text_output.show_nesting_p ()) + || text_output.show_locations_in_nesting_p ()); + char *indent = text_output.build_indent_prefix (true); pp_verbatim (text_output.get_printer (), p->list_p () - ? _("%s: In substitution of %qS:\n") - : _("%s: In instantiation of %q#D:\n"), - LOCATION_FILE (location), + ? _("%s%s%sIn substitution of %qS:\n") + : _("%s%s%sIn instantiation of %q#D:\n"), + indent, + show_file ? LOCATION_FILE (location) : "", + show_file ? ": " : "", p->get_node ()); - + free (indent); location = p->locus; p = p->next; } @@ -3842,6 +3850,71 @@ print_location (diagnostic_text_output_format &text_output, "locus", xloc.file, xloc.line); } +/* A RAII class for use when emitting a line of contextual information + via pp_verbatim to a diagnostic_text_output_format to add before/after + behaviors to the pp_verbatim calls. + + If the text output has show_nesting_p (), then the ctor prints + leading indentation and a bullet point, and the dtor prints + the location on a new line, and calls diagnostic_show_locus, both + with indentation (and no bullet point). + + Otherwise (when the text output has !show_nesting_p), the ctor prints + the location as leading information on the same line, and the + dtor optionally calls diagnostic_show_locus. */ + +class auto_context_line +{ +public: + auto_context_line (diagnostic_text_output_format &text_output, + location_t loc, + bool show_locus = false) + : m_text_output (text_output), + m_loc (loc), + m_show_locus (show_locus) + { + char *indent = m_text_output.build_indent_prefix (true); + pp_verbatim (m_text_output.get_printer (), indent); + free (indent); + if (!m_text_output.show_nesting_p ()) + print_location (m_text_output, m_loc); + } + ~auto_context_line () + { + pretty_printer *const pp = m_text_output.get_printer (); + if (m_text_output.show_nesting_p ()) + { + if (m_text_output.show_locations_in_nesting_p ()) + { + char *indent = m_text_output.build_indent_prefix (false); + pp_verbatim (pp, indent); + print_location (m_text_output, m_loc); + pp_newline (pp); + + char *saved_prefix = pp_take_prefix (pp); + pp_set_prefix (pp, indent); + gcc_rich_location rich_loc (m_loc); + diagnostic_show_locus (&m_text_output.get_context (), &rich_loc, + DK_NOTE, pp); + pp_set_prefix (pp, saved_prefix); + } + } + else if (m_show_locus) + { + char *saved_prefix = pp_take_prefix (pp); + pp_set_prefix (pp, nullptr); + gcc_rich_location rich_loc (m_loc); + diagnostic_show_locus (&m_text_output.get_context (), &rich_loc, + DK_NOTE, pp); + pp_set_prefix (pp, saved_prefix); + } + } +private: + diagnostic_text_output_format &m_text_output; + location_t m_loc; + bool m_show_locus; +}; + /* Helper function of print_instantiation_partial_context() that prints a single line of instantiation context. */ @@ -3853,7 +3926,7 @@ print_instantiation_partial_context_line (diagnostic_text_output_format &text_ou if (loc == UNKNOWN_LOCATION) return; - print_location (text_output, loc); + auto_context_line sentinel (text_output, loc, true); pretty_printer *const pp = text_output.get_printer (); @@ -3879,10 +3952,6 @@ print_instantiation_partial_context_line (diagnostic_text_output_format &text_ou ? _("recursively required from here\n") : _("required from here\n")); } - gcc_rich_location rich_loc (loc); - char *saved_prefix = pp_take_prefix (pp); - diagnostic_show_locus (&text_output.get_context (), &rich_loc, DK_NOTE, pp); - pp_set_prefix (pp, saved_prefix); } /* Same as print_instantiation_full_context but less verbose. */ @@ -3929,7 +3998,7 @@ print_instantiation_partial_context (diagnostic_text_output_format &text_output, } if (t != NULL && skip > 0) { - print_location (text_output, loc); + auto_context_line sentinel (text_output, loc); pp_verbatim (text_output.get_printer (), _("[ skipping %d instantiation contexts," " use -ftemplate-backtrace-limit=0 to disable ]\n"), @@ -3981,7 +4050,7 @@ maybe_print_constexpr_context (diagnostic_text_output_format &text_output) { const char *s = expr_as_string (t, 0); pretty_printer *const pp = text_output.get_printer (); - print_location (text_output, EXPR_LOCATION (t)); + auto_context_line sentinel (text_output, EXPR_LOCATION (t)); pp_verbatim (pp, _("in %<constexpr%> expansion of %qs"), s); @@ -3994,7 +4063,7 @@ static void print_constrained_decl_info (diagnostic_text_output_format &text_output, tree decl) { - print_location (text_output, DECL_SOURCE_LOCATION (decl)); + auto_context_line sentinel (text_output, DECL_SOURCE_LOCATION (decl)); pretty_printer *const pp = text_output.get_printer (); pp_verbatim (pp, "required by the constraints of %q#D\n", decl); } @@ -4007,7 +4076,7 @@ print_concept_check_info (diagnostic_text_output_format &text_output, tree tmpl = TREE_OPERAND (expr, 0); - print_location (text_output, DECL_SOURCE_LOCATION (tmpl)); + auto_context_line sentinel (text_output, DECL_SOURCE_LOCATION (tmpl)); cxx_pretty_printer *const pp = (cxx_pretty_printer *)text_output.get_printer (); @@ -4031,7 +4100,7 @@ print_constraint_context_head (diagnostic_text_output_format &text_output, tree src = TREE_VALUE (cxt); if (!src) { - print_location (text_output, input_location); + auto_context_line sentinel (text_output, input_location); pretty_printer *const pp = text_output.get_printer (); pp_verbatim (pp, "required for constraint satisfaction\n"); return NULL_TREE; @@ -4059,7 +4128,7 @@ print_requires_expression_info (diagnostic_text_output_format &text_output, if (map == error_mark_node) return; - print_location (text_output, cp_expr_loc_or_input_loc (expr)); + auto_context_line sentinel (text_output, cp_expr_loc_or_input_loc (expr)); cxx_pretty_printer *const pp = static_cast <cxx_pretty_printer *> (text_output.get_printer ()); pp_verbatim (pp, "in requirements "); diff --git a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C new file mode 100644 index 000000000000..5965e25f2563 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C @@ -0,0 +1,41 @@ +// { dg-do compile { target c++17 } } +// { dg-options "-fconcepts" } +// { dg-additional-options "-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-locations=no" } + +struct dog {}; +struct cat {}; + +void pet(dog); +void pet(cat); + +template <class T> +concept has_member_pet = requires(T t) { t.pet(); }; + +template <class T> +concept has_default_pet = T::is_pettable; + +template <class T> +concept pettable = has_member_pet<T> or has_default_pet<T>; + +void pet(pettable auto t); + +struct lizard {}; + +int main() { + pet(lizard{}); // { dg-error "no matching function for call to 'pet\\\(lizard\\\)'" } +} + +/* { dg-begin-multiline-output "" } + * note: candidate: 'template<class auto:1> requires pettable<auto:1> void pet(auto:1)' + * note: template argument deduction/substitution failed: + * note: constraints not satisfied + * In substitution of 'template<class auto:1> requires pettable<auto:1> void pet(auto:1) [with auto:1 = lizard]': + * required from here + * required for the satisfaction of 'pettable<auto:1>' [with auto:1 = lizard] + * note: no operand of the disjunction is satisfied + * note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail + * note: candidate: 'void pet(dog)' + * note: no known conversion for argument 1 from 'lizard' to 'dog' + * note: candidate: 'void pet(cat)' + * note: no known conversion for argument 1 from 'lizard' to 'cat' + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C new file mode 100644 index 000000000000..a4a2f4eb139f --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C @@ -0,0 +1,51 @@ +// { dg-do compile { target c++17 } } +// { dg-options "-fconcepts" } +// { dg-additional-options "-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-locations=no" } +// { dg-additional-options "-fconcepts-diagnostics-depth=3" } + +struct dog {}; +struct cat {}; + +void pet(dog); +void pet(cat); + +template <class T> +concept has_member_pet = requires(T t) { t.pet(); }; + +template <class T> +concept has_default_pet = T::is_pettable; + +template <class T> +concept pettable = has_member_pet<T> or has_default_pet<T>; + +void pet(pettable auto t); + +struct lizard {}; + +int main() { + pet(lizard{}); // { dg-error "no matching function for call to 'pet\\\(lizard\\\)'" } +} + +/* { dg-begin-multiline-output "" } + * note: candidate: 'template<class auto:1> requires pettable<auto:1> void pet(auto:1)' + * note: template argument deduction/substitution failed: + * note: constraints not satisfied + * In substitution of 'template<class auto:1> requires pettable<auto:1> void pet(auto:1) [with auto:1 = lizard]': + * required from here + * required for the satisfaction of 'pettable<auto:1>' [with auto:1 = lizard] + * note: no operand of the disjunction is satisfied + * note: the operand 'has_member_pet<T>' is unsatisfied because + * required for the satisfaction of 'has_member_pet<T>' [with T = lizard] + * required for the satisfaction of 'pettable<auto:1>' [with auto:1 = lizard] + * in requirements with 'T t' [with T = lizard] + * note: the required expression 't.pet()' is invalid, because + * error: 'struct lizard' has no member named 'pet' + * note: the operand 'has_default_pet<T>' is unsatisfied because + * required for the satisfaction of 'has_default_pet<T>' [with T = lizard] + * required for the satisfaction of 'pettable<auto:1>' [with auto:1 = lizard] + * error: 'is_pettable' is not a member of 'lizard' + * note: candidate: 'void pet(dog)' + * note: no known conversion for argument 1 from 'lizard' to 'dog' + * note: candidate: 'void pet(cat)' + * note: no known conversion for argument 1 from 'lizard' to 'cat' + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C new file mode 100644 index 000000000000..0c5cc0152979 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C @@ -0,0 +1,37 @@ +// { dg-do compile { target c++17 } } +// { dg-options "-fconcepts" } +// { dg-additional-options "-fdiagnostics-set-output=text:experimental-nesting=yes,experimental-nesting-show-locations=no" } + +struct dog{}; +struct cat{}; + +void pet(dog); +void pet(cat); + +template <class T> +concept pettable = requires(T t) { t.pet(); }; + +template <pettable T> +void pet(T); + +struct donkey {}; + +int main() { + pet(donkey{}); // { dg-error "no matching function for call to 'pet\\\(donkey\\\)'" } +} + +/* { dg-begin-multiline-output "" } + * note: candidate: 'template<class T> requires pettable<T> void pet(T)' + * note: template argument deduction/substitution failed: + * note: constraints not satisfied + * In substitution of 'template<class T> requires pettable<T> void pet(T) [with T = donkey]': + * required from here + * required for the satisfaction of 'pettable<T>' [with T = donkey] + * in requirements with 'T t' [with T = donkey] + * note: the required expression 't.pet()' is invalid + * note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail + * note: candidate: 'void pet(dog)' + * note: no known conversion for argument 1 from 'donkey' to 'dog' + * note: candidate: 'void pet(cat)' + * note: no known conversion for argument 1 from 'donkey' to 'cat' + { dg-end-multiline-output "" } */