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 <[email protected]>
---
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(-)
create mode 100644
gcc/testsuite/g++.dg/concepts/nested-diagnostics-1-truncated.C
create mode 100644 gcc/testsuite/g++.dg/concepts/nested-diagnostics-1.C
create mode 100644 gcc/testsuite/g++.dg/concepts/nested-diagnostics-2.C
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 220ac130b0b4..db9eb1a55cfc 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -3887,18 +3887,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,
@@ -3906,13 +3906,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);
}
}
@@ -3926,13 +3926,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);
}
@@ -3988,11 +3988,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)
@@ -4009,13 +4010,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);
@@ -4026,33 +4027,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;
@@ -4125,6 +4132,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)
@@ -8337,7 +8346,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,
@@ -8356,7 +8365,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,
@@ -8431,7 +8440,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 8a36b9c88c4d..c25ba7893552 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1452,6 +1452,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
@@ -2360,6 +2361,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);
@@ -2371,6 +2373,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);
}
}
@@ -3421,6 +3424,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 23cfee4405ed..b917be2de955 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -3637,6 +3637,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);
@@ -3804,13 +3806,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;
}
@@ -3832,6 +3840,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. */
@@ -3843,7 +3916,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 ();
@@ -3869,10 +3942,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. */
@@ -3919,7 +3988,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"),
@@ -3971,7 +4040,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);
@@ -3984,7 +4053,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);
}
@@ -3997,7 +4066,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 ();
@@ -4021,7 +4090,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;
@@ -4049,7 +4118,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 "" } */
--
2.26.3